Learn Angular 4.0 In 10 Days - Service (Dependency Injection) - Day Seven

Let us start day 7 of "Learning Angular 4.0 in 10 Days" series. In the previous articles, we discussed ngForms and ngContent or Transclusion in Angular 4.0. If you want to read the previous articles of this series, do visit the below links.

What is Service?

An Angular 4 service is simply a JavaScript function, including its related properties and methods which can perform a particular task or a group of tasks. Actually, service is a mechanism to use shared responsibilities within one or multiple components. As we already know, we can create components in Angular 4 and nest multiple components together within a component using Selector. Once our components are nested, we need to manipulate some data within the multiple components. In this case, service is the best way to handle the situation. Service is the best place where we can take data from other source or write down some calculations. Similarly, service can be shared between multiple components as we want.

Angular 4.0 has greatly simplified the concept of service over Angular 1.x. In Angular 1, there were service, factory, provider, delegate, value etc. but it was not always clear when to use which one. So for that reason, Angular 4 simply changes the concept of the Angular 1.0. There are simply two steps for creating Service in Angular 4.

  1. Create a class with @Injectable decorator.
  2. Register the class with provider or inject the class by using dependency injection.

In Angular 4.0, a service is basically used when a common functionality or business logic needs to be provided or written and needs to be shared with a different name. Actually, service is totally a reusable object, assuming that our Angular application contains some of the components performing the logging for error tracking purposes. In such a case, you will end up having an error log method in each of these components. This obviously is a bad design approach as the error log method is duplicated across components. If you want to change the semantics of error logging then you will need to change the code in all these components which in turn will impact the whole application. So a good design approach will be to have a common service component performing the logging feature. The log method in each of the components will be removed and placed in the specific logging service class. The components can use the logging feature by injecting the logging service. Service injection is one form of dependency injection provided by Angular.

Design Considerations

In Angular 4.0, Services can be used to interact with data or implement business logic as per requirement. It can be used to provide connection to database or define functions that operate on data like CRUD operations. One can also design the service to provide a remote access to a web resource. It can be used to provide common integration needs like invoking a API of another application. Services can also be an excellent choice to design communication between components of your Angular application. You can build a service that provides a common event handling platform. Last but not least, one can centralize common business functions used across the application as services. There are various approaches one can take to design a service. While designing services, think common functionality that every aspect of your application uses and transform them into services.

Create a Service

So we can create a custom service as per our requirement. For creating a service, we need to follow the below steps.

  1. First we need to create a type script file with proper naming.
  2. Now Create a type script class with the proper name which will represent the service after a while.
  3. Use @Injectable decorator at the beginning of the class name which imported from the @angular/core packages. Basically the meaning of @Injectable is that custom service and any of its dependencies can be automatically injected by the other components.
  4. Though for design readability, Angular recommends for you to always define the @Injectable decorator whenever you create any service.
  5. Now, use the Export keyword against the class objects so that this service can be injectable or reused any other components.
One more important thing is that when we create our custom service available to the whole application through the use of providers meta data. This provider's meta data must be defined in the app.module.ts (main application module file) file. If you provide the service in a main module file then it is visible to whole application. If you provide it in any component then only that component can use the service. By providing the service at the module level, Angular creates only one instance of CustomService class which can be used by all the components in an application.
  1. import { Injectable } from '@angular/core';  
  2. @Injectable()  
  3. export class AlertService {  
  4.   
  5.     alert(message: string) {  
  6.         alert(message);  
  7.     }  
  8.   
  9.     constructor() { }  
  10.   
  11. }  

@Injectable

@Injectable is actually is a decorator. Decorator are a proposed extension in javascript. In short, decorator provides the facility to programmer to modify or use methods, classes, properties and parameters. Injectables are just normal classes (normal objects) and as such, they have no special lifecycle. When an object of your class is created, the class’s constructor is called, so that’s what your “OnInit” would be. As for the destruction, a service does not really get destroyed. The only thing that might happen is that it gets garbage collected once there is no longer a reference to it, which likely happens after the dependency injector is removed itself. But you generally have no control over it, and there is no concept of a deconstructor in JavaScript.

@Injectable() lets Angular know that a class can be used with the dependency injector. @Injectable()is not strictly required if the class has other Angular decorators on it or does not have any dependencies. What is important is that any class that is going to be injected with Angular is decorated. However, best practice is to decorate injectables with @Injectable(), as it makes more sense to the reader.

  1. @Injectable()  
  2. export class SampleService {  
  3.     constructor() {  
  4.         console.log('Sample service is created');  
  5.     }  
  6. }  

Just like most of the other server and client side frameworks, Angular 4 also has the concept of Dependency Injection or DI. Instances of services or classes can be injected using the constructor. Angular provides the concept of providers meta data to provide a service or any class as a candidate for automatic injection, at runtime, by defining that service or a class in a constructor of the dependent class.

What is Dependency Injection

Actually dependency injection is an important and useful application design pattern. Angular 4.0 has its own dependency injection framework. Basically, it is a coding pattern in which classes receive their dependencies from external sources rather than creating them himself. In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. Dependency Injection (DI) is a software design pattern that deals with how components get hold of their dependencies. The AngularJS injector subsystem is in charge of creating components, resolving their dependencies, and providing them to other components as requested.

Dependency Injection in Angular 4.0

Dependency injection has always been one of Angular’s biggest features and selling points. It allows us to inject dependencies in different components across our applications, without needing to know how those dependencies are created, or what dependencies they need themselves. However, it turns out that the current dependency injection system in Angular 1.x has some problems that need to be solved in Angular 4, in order to build the next generation framework. The below picture demonstrates the angular 4.0 dependency injection process.

Actually from the beginning, Dependency Injection is one of the biggest key features and selling points in Angular Js. It allows us to inject dependencies in different components across our applications, without needing to know how those dependencies are created, or what dependencies they need themselves. However, it turns out that the current dependency injection system in Angular 1.x has some problems that need to be solved in Angular 4, in order to build the next generation framework. In this article, we’re going to explore the new dependency injection system for future generations. So before going to create the custom service in the example, we first must understand what dependency injection is, and what are the advantages or disadvantages in Angular.

Dependency Injection as a Design Pattern

In ng-conf 2014, Vojta Jina gives a great speech on the dependency injection and also describes the story about the new dependency injection in the AngularJs. Basically we can use dependency injection in two ways – as a design pattern and as a framework. So first we will discuss about dependency injection as a pattern. We first look at the below code and then analyze the problems it introduces –

  1. class car   
  2. {  
  3.     constructor()   
  4.     {  
  5.         this.engine = new Engine();  
  6.             this.tires = Tires.getInstance();  
  7.             this.color = app.get('colors');  
  8.     }  
  9. }  

Nothing special here. We have a class Car that has a constructor in which we set up everything we need in order to construct a car object once it's needed. But what is the problem? Well, as we can see, the constructor not only assigns needed dependencies to internal properties, it also knows how those object are created. For example the engine object is created using the Engine constructor, Tires seems to be a singleton interface and the doors are requested via a global object that acts as a service locator.

This leads to code that is hard to maintain and even harder to test. Just imagine you’d like to test this class. How would you replace Engine with a MockEngine dependency in that code? When writing tests, we want to test different scenarios that our code is used in, hence each scenario needs its own configuration. If we want to write testable code, we need to write reusable code. Our code should work in any environment as long as all dependencies are satisfied. Which brings us to the conclusion that testable code is reusable code and vise versa.

So how can we write this code better and make it more testable? It’s super easy and you probably already know what to do. We change our code to this:

  1. class Car {  
  2.     constructor(engine, tires, colors) {  
  3.         this.engine = engine;  
  4.         this.tires = tires;  
  5.         this. colors = colors;  
  6.     }  
  7. }  

All we did is we moved the dependency creation out of the constructor and extended the constructor function to expect all needed dependencies. There are no concrete implementations anymore in this code, we literally moved the responsibility of creating those dependencies to a higher level. If we want to create a car object now, all we have to do is to pass all needed dependencies to the constructor:

  1. var car = new car(new Engine(), new Tires(), new Colors);  

How cool is that? The dependencies are now decoupled from our class, which allows us to pass in mocked dependencies in case we’re writing tests:

  1. var car = new car(new MockEngine(), new MockTires(), new MockColors);  

And guess what, this is dependency injection. To be a bit more specific, this particular pattern is also called constructor injection. There are two other injection patterns, setter injection and interface injection, but we won’t cover them in this article.

Okay cool, now we use DI, but when does a DI system come into play? As mentioned before, we literally moved the responsibility of dependency creation to a higher level. And this is exactly what our new problem is. Who takes care of assembling all those dependencies for us? It’s us.

  1. function main() {  
  2.         var engine = new Engine();  
  3.         var tires = new Tires();  
  4.         var colos = new Colors();  
  5.         var car = new Card(engine,tires,colors);  
  6.         car.drive();  
  7.     }  

We need to maintain a main function now. Doing that manually can be quite hairy, especially when the application gets bigger and bigger. Wouldn’t it be nice if we could do something like this?

  1. function main() {  
  2.         var injector = new Injector(…………….);  
  3.         var car = injector.get(Car);  
  4.         car.drive();  
  5.     }  

Dependency Injection as a Framework

This is where dependency injection as a framework comes in. As we all know, Angular 4 has its own DI system which allows us to annotate services and other components and let the injector find out what dependencies need to be instantiated. This is all cool but it turns out, that the existing DI has some problem though:

  • Internal cache
    Dependencies are served as singletons. Whenever we ask for a service, it is created only once per application lifecycle. Creating factory machinery is quite hairy.

  • Namespace collision
    There can only be one token of a “type” in an application. If we have a car service, and there’s a third-party extension that also introduces a service with the same name, we have a problem.

  • Built into the framework
    Angular 4’s DI is baked right into the framework. There’s no way for us to use it decoupled as a standalone system. 

Dependency Injection basically consists three things,

  1. Injector – The Injector object that exposes APIs to us to create instances of dependencies
  2. Provider – A Provider is like a commander that tells the injector how to create an instance of a dependency. A provider takes a token and maps that to a factory function that creates an object.
  3. Dependency – A Dependency is the type of  object that should be created.

So as per the above discussion, dependency injection is responsible for the below issues,

  • Registering a class, function or value. These items, in the context of dependency injection, are called “providers” because they result in something. For example, a class is used to provide or result in an instance – see below for more details on provider types
  • Resolving dependencies between providers – for example, if one provider requires another provider
  • Making the provider’s result available in code when we ask for it. This process of making the provider result available to a block of code is called “injecting it.” The code that injects the provider results is, logically enough, called an “injector.”
  • Maintaining a hierarchy of injectors so that if a component asks for a provider result from a provider not available in its injector, DI searches up the hierarchy of injectors

What are Providers?

So now one question arises after the above discussion, which is, what are these providers that injectors register in each level? Basically it is very simple – a provider is a resource or javascript thing or class that Angular uses to provide something we want to use :

  • A class provider generates or provides an instance of a class
  • A factory provider generates or provides whatever returns when we run specified function
  • A value provider does not need to take up action to provide the result, it just returns value.
 Sample of a Class
  1. export class testClass {  
  2.     public message:string = ‘Hello from Service Class’;  
  3.     public count:number;  
  4.     constructor(){  
  5.         this.count=1;  
  6.     }  
  7. }  

Okay, that’s the class; now let’s instruct Angular to use it to register a class provider so we can ask the dependency injection system to give us an instance to use in our code. We’ll create a component, that will serve as the root component for our application. Adding the testClass provider to this component is straightforward,

  • Import testClass
  • Add it to the @Component providers property
  • Add an argument of type “testClass” to the constructor
  1. import { Component } from ‘@angular/core’;  
  2. import { testClass} from ‘./service/testClass’;  
  3.   
  4. @Component({  
  5. module : module.id,  
  6. selector : ‘test-prog’,  
  7. template : ‘<h1>Hello {{_message}}</h1>’,  
  8. providers : [testClass]  
  9. })  
  10. export class TestComponent{  
  11.     private _message:string=’’;  
  12.     constructor(private _testClass : testClass){  
  13.         this._message = this._testClass.message;  
  14.     }  
  15. }  

Under the covers, when Angular instantiates the component, the DI system creates an injector for the component which registers the testClass provider. Angular then sees the testClass type specified in the constructor’s argument list and looks up the newly registered testClass provider and uses it to generate an instance which it assigns to “_testClass”. The process of looking up the testClass provider and generating an instance to assign to “_testClass” is all Angular. It takes advantage of the TypeScript syntax to know what type to search for but Angular’s injector does the work of looking up and returning the testClass instance.

Sample Code of app.component.student.ts
  1. import { Component, OnInit, ViewChild } from '@angular/core';  
  2. import { StudentService } from './app.service.student';  
  3.   
  4. @Component({  
  5.     moduleId: module.id,  
  6.     selector: 'student-details',  
  7.     templateUrl: 'app.component.student.html'  
  8. })  
  9.   
  10. export class StudentComponent implements OnInit {  
  11.   
  12.     private _model: any = {};  
  13.     private _source: Array<any>;  
  14.   
  15.     constructor(private _service: StudentService) {  
  16.         this._source = this._service.returnStudentData();  
  17.     }  
  18.   
  19.     ngOnInit(): void {  
  20.     }  
  21.   
  22.     private submit(): void {  
  23.         if (this.validate()) {  
  24.             this._service.addStudentData(this._model);  
  25.             this.reset();  
  26.         }  
  27.     }  
  28.   
  29.     private reset(): void {  
  30.         this._model = {};  
  31.     }  
  32.   
  33.     private validate(): boolean {  
  34.         let status: boolean = true;  
  35.         if (typeof (this._model.name) === "undefined") {  
  36.             alert('Name is Blank');  
  37.             status = false;  
  38.             return;  
  39.         }  
  40.         else if (typeof (this._model.age) === "undefined") {  
  41.             alert('Age is Blank');  
  42.             status = false;  
  43.             return;  
  44.         }  
  45.         else if (typeof (this._model.city) === "undefined") {  
  46.             alert('City is Blank');  
  47.             status = false;  
  48.             return;  
  49.         }  
  50.         else if (typeof (this._model.dob) === "undefined") {  
  51.             alert('dob is Blank');  
  52.             status = false;  
  53.             return;  
  54.         }  
  55.         return status;  
  56.     }  
  57. }  
Sample Code of app.component.student.html
  1. <div>  
  2.     <h2>Student Form</h2>  
  3.     <table style="width:80%;">  
  4.         <tr>  
  5.             <td>Student Name</td>  
  6.             <td><input type="text" [(ngModel)]="_model.name" /></td>  
  7.         </tr>  
  8.         <tr>  
  9.             <td>Age</td>  
  10.             <td><input type="number" [(ngModel)]="_model.age" /></td>  
  11.         </tr>  
  12.         <tr>  
  13.             <td>City</td>  
  14.             <td><input type="text" [(ngModel)]="_model.city" /></td>  
  15.         </tr>  
  16.         <tr>  
  17.             <td>Student DOB</td>  
  18.             <td><input type="date" [(ngModel)]="_model.dob" /></td>  
  19.         </tr>  
  20.         <tr>  
  21.             <td></td>  
  22.             <td>  
  23.                 <input type="button" value="Submit" (click)="submit()" />     
  24.                 <input type="button" value="Reset" (click)="reset()" />  
  25.             </td>  
  26.         </tr>  
  27.     </table>  
  28.     <h3>Student Details</h3>  
  29.     <div class="ibox-content">  
  30.         <div class="ibox-table">  
  31.             <div class="table-responsive">  
  32.                 <table class="responsive-table table-striped table-bordered table-hover">  
  33.                     <thead>  
  34.                         <tr>  
  35.                             <th style="width:40%;">  
  36.                                 <span>Student's Name</span>  
  37.                             </th>  
  38.                             <th style="width:15%;">  
  39.                                 <span>Age</span>  
  40.                             </th>  
  41.                             <th style="width:25%;">  
  42.                                 <span>City</span>  
  43.                             </th>  
  44.                             <th style="width:20%;">  
  45.                                 <span>Date of Birth</span>  
  46.                             </th>  
  47.                         </tr>  
  48.                     </thead>  
  49.                     <tbody>  
  50.                         <tr *ngFor="let item of _source; let i=index">  
  51.                            <td><span>{{item.name}}</span></td>  
  52.                             <td><span>{{item.age}}</span></td>  
  53.                             <td><span>{{item.city}}</span></td>  
  54.                             <td><span>{{item.dob}}</span></td>  
  55.                         </tr>  
  56.                     </tbody>  
  57.                 </table>  
  58.             </div>  
  59.         </div>  
  60.     </div>  
  61.   
  62. </div>  
Sample code of app.service.student.ts

The main objective of this service is to use this as a datasource of the studnet list. User can add this student's data from entry form, and those data are kept in an array of objects within the student service.
  1. import { Injectable } from "@angular/core";  
  2.   
  3. @Injectable()  
  4. export class StudentService {  
  5.     private _studentList: Array<any> = [];  
  6.   
  7.     constructor() {  
  8.         this._studentList = [{name:'Amit Roy', age:20, city:'Kolkata', dob:'01-01-1997'}];  
  9.     }  
  10.   
  11.     returnStudentData(): Array<any> {  
  12.         return this._studentList;  
  13.     }  
  14.   
  15.     addStudentData(item: any): void {  
  16.         this._studentList.push(item);  
  17.     }  
  18. }  
Sample Code of app.article.module.ts 
  1. import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';  
  2. import { APP_BASE_HREF } from '@angular/common';  
  3. import { BrowserModule } from '@angular/platform-browser';  
  4. import { FormsModule } from "@angular/forms";  
  5. import { HttpModule } from '@angular/http';  
  6. import { RouterModule } from '@angular/router';  
  7.   
  8. import { HomeComponent } from './SampleCode/app.component.home';  
  9. import { StudentService } from './samplecode/app.service.student';  
  10. import { StudentComponent } from './samplecode/app.component.student';  
  11.   
  12.   
  13. @NgModule({  
  14.     imports: [BrowserModule, FormsModule, HttpModule],  
  15.     declarations: [HomeComponent, StudentComponent],  
  16.     bootstrap: [HomeComponent],  
  17.     schemas: [NO_ERRORS_SCHEMA],  
  18.     providers: [{ provide: StudentService, useClass: StudentService }]  
  19. })  
  20.   
  21. export class ArticleModule { }  
                                                                                             Read Part 8 of this article here>>


Similar Articles