In this tutorial, you will learn what angular services are and how they are used.
We will take a look at the benfits of services by using simple examples.
That way you will learn about dependency injection, how to provide a serive and what singleton services are.
Let's get started!
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.
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.
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.
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.
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.
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.
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.
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) }) } }
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
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.
In this tutorial, we learned what angular services are and how to use them.
I hope you enjoyed this read.
If you did please share this post!
Thanks for reading!
Subscribe to the newsletter
We save your email address, your name and your profile picture on our servers when you sing in. Read more in our Privacy Policy.