Introduction To Services In Angular 2

Every dynamic application needs to access the data from some external source, which can be from some database or from other resources. To access the data from the external sources in Angular 2, Service is the answer. Services offer reusability; i.e. , they can be used throughout the application across the components. Also, adding the Services to Angular application makes your component free from data access code & more focus oriented on displaying view logic.

Service in Angular2 has the features given below.

  1. They are singleton objects (i.e. only one instance of Angular Service exists throughout the Application).
  2. They are capable of returning the data in the form of promises (who is quite familiar with Angular1.x) as well as in the form of Observables (which is the next generation JavaScript feature).
  3. Service class is decorated with @Injectable() decorator (The @Injectable() decorator is required only if our service class is making use of some Angular injectables like http service within it).

NOTE

You can register your Service either globally, at the module level or at the component level.

To demonstrate the Service, we’ll separate employee collection, which has for now been directly loaded inside the EmpListComponent to our new service EmpService & now our Service will be responsible to return the data to the EmpListComponent. Hence, let’s create a new Service with the name “emp.service.ts” inside our “emp” folder.

emp.service.ts 

  1. import { Injectable } from '@angular/core';  
  2. import { IEmployee } from './employee';  
  3.   
  4. @Injectable()  
  5. export class EmpService {  
  6.     constructor() {  
  7.     }  
  8.           
  9.     public getEmployees(): IEmployee[] {  
  10.         return [  
  11.             {  
  12.                 "empId""E001",  
  13.                 "name""Leanne Graham",  
  14.                 "email""Sincere@april.biz",  
  15.                 "phone""1-770-736-8031 x56442",  
  16.                 "photo""./app/assets/imgs/face1.jpg",  
  17.                 "salary": 50000,  
  18.                 "appraisalRating": 4  
  19.             },  
  20.             {  
  21.                 "empId""E002",  
  22.                 "name""Ervin Howell",  
  23.                 "email""Shanna@melissa.tv",  
  24.                 "phone""210.067.6132",  
  25.                 "photo""./app/assets/imgs/face2.jpg",  
  26.                 "salary": 65000,  
  27.                 "appraisalRating": 3.5  
  28.             },  
  29.             {  
  30.                 "empId""E003",  
  31.                 "name""Clementine Bauch",  
  32.                 "email""Nathan@yesenia.net",  
  33.                 "phone""1-477-935-8478 x6430",  
  34.                 "photo""./app/assets/imgs/face3.jpg",  
  35.                 "salary": 62500,  
  36.                 "appraisalRating": 3.75  
  37.             },  
  38.             {  
  39.                 "empId""E004",  
  40.                 "name""Patricia Lebsack",  
  41.                 "email""Julianne.OConner@kory.org",  
  42.                 "phone""(254)954-1289",  
  43.                 "photo""./app/assets/imgs/face4.png",  
  44.                 "salary": 49500,  
  45.                 "appraisalRating": 4.25  
  46.             },  
  47.             {  
  48.                 "empId""E005",  
  49.                 "name""Chelsey Dietrich",  
  50.                 "email""Lucio_Hettinger@annie.ca",  
  51.                 "phone""493-170-9623 x156",  
  52.                 "photo""./app/assets/imgs/face5.jpg",  
  53.                 "salary": 25000,  
  54.                 "appraisalRating": 3.25  
  55.             },  
  56.             {  
  57.                 "empId""E006",  
  58.                 "name""Mrs. Dennis Schulist",  
  59.                 "email""Karley_Dach@jasper.info",  
  60.                 "phone""1-463-123-4447",  
  61.                 "photo""./app/assets/imgs/face6.ico",  
  62.                 "salary": 100000,  
  63.                 "appraisalRating": 4.5  
  64.             },  
  65.             {  
  66.                 "empId""E007",  
  67.                 "name""Kurtis Weissnat",  
  68.                 "email""Telly.Hoeger@billy.biz",  
  69.                 "phone""010-692-6593 x09125",  
  70.                 "photo""./app/assets/imgs/face7.png",  
  71.                 "salary": 36900,  
  72.                 "appraisalRating": 4  
  73.             }  
  74.         ];  
  75.     }  
  76. }   

Since our Service is ready, it’s the time to register the Service class. To register, we’ll be using “Providers” property/option of @NgModule. Our updated file is given below.

app.module.ts file 

  1. /** Importing the angular modules from namespace */  
  2. import { NgModule } from '@angular/core';  
  3. import { FormsModule } from '@angular/forms';  
  4. import { BrowserModule } from '@angular/platform-browser';  
  5. /** Importing the AppComponent from the application's app folder */  
  6. import { AppComponent } from './app.component';  
  7. import { EmployeeListComponent } from './emp/emp-list.component';  
  8. import { EmployeeSearchPipe } from './emp/emp-search.pipe';  
  9. import { FormatSalaryDirective } from './emp/emp-formatsalary.directive';  
  10. import { ARStarComponent } from './shared/ar-star.component';  
  11. import { EmpService } from './emp/emp.service';  
  12.   
  13. @NgModule({  
  14.   imports: [BrowserModule, FormsModule],//other modules whose exported classes are needed by component templates declared in this module.  
  15.   declarations: [AppComponent, EmployeeListComponent, EmployeeSearchPipe, ARStarComponent, FormatSalaryDirective],// the view classes that belong to this module. Angular has three kinds of view classes: components, directives, and pipes.  
  16.   providers: [EmpService],  
  17.   bootstrap: [AppComponent]//the main application view, called the root component, that hosts all other app views. Only the root module should set this bootstrap property.  
  18. })  
  19. export class AppModule { }   

Try to run the Application and you’ll find that our app is still working, as expected.


Our Service class is decorated with @Injectable, which is not really required, since we are not using any Angular injectables now. Hence, even if you comment that line, our application still works perfectly. The snapshot is given below after commenting @Injectable() decorator from our Service class.


Our Service class still has static data loaded into the array. We can make it a little more complex by adding our data into a JSON file & via Http request, we can try to load the data. This will be a good demonstration of how to create a realistic Angular2 Service.

I’ve created a new “emp-data.json” file inside our emp folder & copied our array collection from our Service to that JSON file. Now, inside our EmpService class, we’ll make use of httpService to load the contents of this file. Http Service lies inside HttpModule, so our first step for using Http Service would be to load the HttpModule & register it with @NgModule in the “imports” section inside our app.module.ts file.

Here, our updated app.module.ts file is given below. 

  1. /** Importing the angular modules from namespace */  
  2. import { NgModule } from '@angular/core';  
  3. import { FormsModule } from '@angular/forms';  
  4. import { BrowserModule } from '@angular/platform-browser';  
  5. import { HttpModule } from '@angular/http';  
  6. /** Importing the AppComponent from the application's app folder */  
  7. import { AppComponent } from './app.component';  
  8. import { EmployeeListComponent } from './emp/emp-list.component';  
  9. import { EmployeeSearchPipe } from './emp/emp-search.pipe';  
  10. import { FormatSalaryDirective } from './emp/emp-formatsalary.directive';  
  11. import { ARStarComponent } from './shared/ar-star.component';  
  12. import { EmpService } from './emp/emp.service';  
  13.   
  14. @NgModule({  
  15.   imports: [BrowserModule, FormsModule, HttpModule],//other modules whose exported classes are needed by component templates declared in this module.  
  16.   declarations: [AppComponent, EmployeeListComponent, EmployeeSearchPipe, ARStarComponent, FormatSalaryDirective],// the view classes that belong to this module. Angular has three kinds of view classes: components, directives, and pipes.  
  17.   providers: [EmpService],  
  18.   bootstrap: [AppComponent]//the main application view, called the root component, that hosts all other app views. Only the root module should set this bootstrap property.  
  19. })  
  20. export class AppModule { }   

Now, we’ve HttpModule registered with our app module. We can access it anywhere within our application. The updated code is given below.

emp.service.ts 

  1. import { Injectable } from '@angular/core';  
  2. import { Http } from '@angular/http';  
  3. import { IEmployee } from './employee';  
  4.   
  5. @Injectable()  
  6. export class EmpService {  
  7.   
  8.     constructor(private http: Http) {  
  9.     }  
  10.    ...  
  11. }   

NOTE

Now, we are injecting http Angular Service. We need to have @Injectable decorated above our Service class.

In Angular2, the Service can either return the data in the form of Promises or in the form of Observables. In this demo, we’ll focus more on how to return the data through observables. You can think of Observables as an array, whose elements come asynchronously over a period of time. Observables are next JavaScript features; i.e., to make use of Observables in the current application, we’ll have to make use of some 3rd party library & the name of the library is Rx/Js. This library provides the support for Observables & its methods. For more information about the Rx/Js library, you can checkout this link.

There are certain differences between Observables & Promises.

  1. Through Promise, we can return only a single value at a time but through Observables, we can return multiple values.
  2. Observables are more advanced than Promises; i.e. they are cancellable & Promises are not

The points given above are clearly mentioned in this video.

Since we’ll be using Rx/Js library throughout the application, I’m importing this library in app.module.ts file, using import statement given below.

import 'rxjs/Rx'; // this will load the whole Rx/Js library

Since our Rx/Js library is loaded, we can use Observables throughout our application, wherever required. The updated code for EmpService is given below.

emp.service.ts file 

  1. import { Injectable } from '@angular/core';  
  2. import { Http, Response } from '@angular/http';  
  3. import { IEmployee } from './employee';  
  4. import { Observable } from 'rxjs/Observable';  
  5.   
  6. @Injectable()  
  7. export class EmpService {  
  8.   
  9.     private _repositoryURL: string = "app/emp/emp-data.json";  
  10.   
  11.     constructor(private http: Http) {  
  12.     }  
  13.   
  14.     public getEmployees(): Observable<IEmployee[]> {  
  15.         return this.http.get(this._repositoryURL)  
  16.             .map((response: Response) => { return <IEmployee[]>response.json() })  
  17.             .catch(this.handleError);  
  18.     }  
  19.   
  20.     private handleError(errorResponse: Response) {  
  21.         console.log(errorResponse.statusText);  
  22.         return Observable.throw(errorResponse.json().error || "Server error");  
  23.     }  
  24. }   

You can see return type of getEmployees() method is Observable<IEmployee[]> & from within that function, we are returning employee[] by typecasting JSON data to IEmployee[] type. We are making use of Rx/Js library map function to transform JSON data to IEmployee[]. Map function helps us to transform the Observable values before they're returned. For more information on other Rx/Js library function, refer the link.

Now our Service class is returning Observables. To access Observables data, we need to subscribe/register handler for handling the emitted values. Here is the updated code for EmpListComponent.

emp-list.component.ts 

  1. import { Component, OnInit } from '@angular/core';  
  2. import { IEmployee } from './employee';  
  3. import { EmpService } from './emp.service';  
  4.   
  5. @Component({  
  6.     selector: 'emp-list',  
  7.     templateUrl: './app/emp/emp-list.component.html'  
  8. })  
  9. export class EmployeeListComponent implements OnInit {  
  10.     pageTitle: string = "Employee Details";  
  11.     imageWidth: number = 50;  
  12.     imageHeight: number = 50;  
  13.     filterByName: string = "";  
  14.     showImageColumn: boolean = false;  
  15.     eRating: string = "";  
  16.     employees: IEmployee[];  
  17.     errorMessage: string;  
  18.   
  19.     constructor(private empService: EmpService) {  
  20.         this.employees = [];  
  21.     }  
  22.   
  23.     ngOnInit(): void {  
  24.         let self = this;  
  25.         self.empService.getEmployees().subscribe(empData => this.employees = empData,  
  26.             error => this.errorMessage = <any>error);  
  27.     }  
  28.   
  29.     /** 
  30.      * Toggles employee image 
  31.      */  
  32.     toggleImageColumn(): void {  
  33.         this.showImageColumn = !this.showImageColumn;  
  34.     }  
  35.   
  36.     /** 
  37.      * Rating Clicked Handler 
  38.      */  
  39.     ratingClickedHandler(message: string): void {  
  40.         let self = this;  
  41.         self.eRating = message;  
  42.     }  
  43. }   

Try to run the application and it works as expected.

In our next post, we’ll continue further. Until then, you can download the solution and test it locally at your end. I am uploading the solution file with this post; but I won’t be able to upload the node_modules folder because of the heavy size. Thus, I request you install NPM before you run the application.