Skip to main content

Angular Services

angular-question

What are services?

Services solve one big problem: They prevent us from copying logic over and over. Instead, they centralize business logic.
Also, they are very useful everywhere in our application.
That is because they can be easily requested via Dependency Injection.
They are also very useful if you want to use the same instance of a class everywhere in your class.

Services are just classes

After all, services are just classes. Other than components, services may only contain logic. They should be completely separated from the view (anything visual). They also should only fulfill one purpose, following the single responsibility principle.

Use Cases

The most common use case is I/O (Input/Output). To get more specific: HTTP requests. Generally, all HTTP requests in Angular are wrapped by a service. Why? Because with the help of Dependency Injection, our code stays highly maintainable. Here is an example:
Imagine you changed the route of your REST-Endpoint. Imagine you called that route in a billion different places. Good luck finding and replacing them all!
By wrapping our Http calls in a service, we know exactly where the change has to be made. And it is only one line affected.
angular-compiler-banner

Caching example

My favorite example for a service is a cached HTTP service. So let's build one together!
So let's say we want to create a wrapper service for a specific REST-Endpoint. Furthermore, we only want to hit the web once. Every additional response is served from a cache.
First, we define what kind of object our endpoint returns. In this case, we do so, by defining the Whatever interface.
src/app/services/cached.http.service.ts
interface Whatever {
  id: string
}
In this case, our response object only contains an id. Of course, this object can look however you want.
Next, we create our service. It is a simple injectable with the public getWhatever method. It also has a cache object, where we save the responses we got. Finally, we have a private getWhateverHttpRequest that does the actual Http request.
src/app/services/cached.http.service.ts
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs/Observable'
import { HttpClient } from '@angular/common/http'

// of-operator has to be imported separatly
import 'rxjs/add/observable/of'
import 'rxjs/add/operator/first'

interface Whatever {
  id: string
}

@Injectable()
export class CachedHttpService {
  private cache = {}

  // Note: I'm using the 4.3 Http client here.
  constructor(private http: HttpClient) {}

  public getWhatever(id: string): Observable<Whatever> {
    if (this.cache[id] == null) {
      // If we have no response in chache, reach out to the web
      const observable = this.getWhateverHttpRequest(id)

      // We need to subscribe to the result, to write the result to our cache
      let subscription = observable.first().subscribe(response => {
        // Wite the response to cache
        this.cache[id] = response
      })

      console.log('Cached Http: Read from server')
      return observable
    } else {
      //If we have the response in our cache already, just serve that response
      console.log('Cached Http: Read from cache')
      return Observable.of(this.cache[id])
    }
  }

  private getWhateverHttpRequest(id: string): Observable<Whatever> {
    // Only for test purposes
    return Observable.of({ id: 'result' })
    // return this.http.get<Whatever>("anyurl.com/api/any/" + id);
  }
}
Every class (or service) served via Dependency Injection, has to be provided somewhere. We do so in our AppModule. We also need to import the HttpClientModule here, if we actually want to make any requests.
I'm using the HttpClient of Angular version 4.3+. If you are on older versions, you can use the old client, as well. However, the syntax might look a little different.
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core'

import { AppComponent } from './app.component'
import { HttpClientModule } from '@angular/common/http'
import { CachedHttpService } from './services/cached.http.service'

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule],
  providers: [CachedHttpService], // provide our service here
  bootstrap: [AppComponent],
})
export class AppModule {}
All that is left now, is to actually use our service somewhere. We do so in our AppComponent.
src/app/app.component.ts
import { Component, OnInit } from '@angular/core'
import { CachedHttpService } from './services/cached.http.service'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  title = 'app'
  constructor(private cachedHttpService: CachedHttpService) {}

  ngOnInit(): void {
    this.cachedHttpService.getWhatever('1').subscribe(result => {
      console.log('Received Response: ' + result.id)
    })

    this.cachedHttpService.getWhatever('1').subscribe(result => {
      console.log('Received Response: ' + result.id)
    })

    this.cachedHttpService.getWhatever('1').subscribe(result => {
      console.log('Received Response: ' + result.id)
    })
  }
}

Output

As you can see, only the first request is actually hitting the web. Exactly what we wanted to achieve:
Cached Http: Read from server
Received Response: result

Cached Http: Read from cache
Received Response: result

Cached Http: Read from cache
Received Response: result

Singleton services

If a service is a singleton, there is only one instance of that service for the whole app.
With singleton services there is an alternative wayof providing the service. Instead of using the providers-array of the @NgModule, we can tell the @Injectable where the service should be provided.
This is done by passing the provideIn option to the @Injectable decorator:
import { Injectable } from '@angular/core'

@Injectable({
  providedIn: 'root',
})
export class CachedHttpService {}
In this case, the service is provided in "root". That means it is provided applicaition-wide. It can also be provided in a specific module.
The provideIn feature is available in Angular 6.0.0 or higher.
Using provideIn over the regular aproach allows angular to tree-shake the service, resulting in a potentially smaller bundle size.

Conclusion

In this tutorial, we learned what angular services are and how to use them.

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...