Learn how Angular’s dependency injection works

Originally published by Neeraj Dana at https://blog.bitsrc.io

Before we start — When building NG apps, it’s better to share your components in a reusable collection, so you won’t have to rewrite them.

No theory — Let’s Code

Suppose we have a component named app component which has a basic and simple structure as follows:

import { Component, OnInit } from "@angular/core";
@Component({
  selector: "my-root",
  templateUrl: "app.component.html",
  styleUrls: ["app.component.css"]
})
export class AppComponent implements OnInit {
  ngOnInit(): void {

}
}

And we have a service class called ‘GreetingService’ with a function ‘sayHello’ that receives ‘name’ as a parameter and returns it (name) with an additional “Hello”, proceeding it.

export class GreetingService{
sayHello(name){
return Hello ${name} ;
}
}

There are two ways to use the service class in the component: first, we can manually create an instance of the service in the component (this is the wrong way and never recommended).

The other way is to let Angular create the instance of our service and pass that instance to our component internally. That is the standard and recommended way.

Injecting our service in the Angular dependency injection system

Now, if you run this project, you will get the error “No provider for GreetingService!”

Import {Component} from ‘@angular/core’;
Import {GreetingService} from ‘. /greetingService’;
@Component({
selector: ‘my-app’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ]
})
export class AppComponent {
constructor(private greetingService : GreetingService){
console.log(this.greetingService.sayHello());
}}

Angular is complaining that it did not find any provider for creating an instance of the greeting service, or it does not know how to create an instance. To let the framework know how the instance will be created, we have to pass a provider object to the provider’s property in the component decorator shown below:

In this provider object, we have many features. Let us understand them one by one.

import { Component } from ‘@angular/core’;
import {GreetingService} from ‘./greetingService’;
@Component({
selector: ‘my-app’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ],
providers:[{

}]
})
export class AppComponent {
constructor(private greetingService : GreetingService){
console.log(this.greetingService.sayHello());
}

}

Custom Factory

Use factory: this will tell the framework which factory will be used while creating the object of the service. In our case, we don’t have any factory. Let’s create one.

The factory will be a function that will be responsible for creating and returning the object of the service.

export function greetingFactory(){
return new GreetingService()
};
Or more short way
export const greetingFactory= () => new GreetingService ();

Custom Injection Token

The next thing is to create a property whose value will be an Injection Token instance. While Using this property, the framework will uniquely identify our service and will inject the right instance of the service.

var greetingTokken = new InjectionToken<GreetingService>(“GREET_TOKEN”);

In the above snippet, we are creating an instance of the InjectionToken class, and it is generic. In our case, the GreetingService instance will inject when someone asks for the injection with the name greetingToken.

For now, our code looks like this:

import { Component ,InjectionToken} from ‘@angular/core’;
import {GreetingService} from ‘./greetingService’;
export const greetingTokken = new InjectionToken<GreetingService>(“GREET_TOKEN”);
export const greetingFactory=()=> new GreetingService();
@Component({
selector: ‘my-app’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ],
providers:[{
provide : greetingTokken,
useFactory : greetingFactory,

}]
})
export class AppComponent {
constructor(private greetingService : GreetingService){
console.log(this.greetingService.sayHello());
}
name = ‘Angular’;
}

But then we also get the same error:

As in the constructor, where we are asking for the instance of our service, we have to tell it the unique string of our injection token that is greetingToken.

Let’s update our code:

export class AppComponent  {
constructor(@Inject(greetingTokken) private greetingService : GreetingService){
console.log(this.greetingService.sayHello(‘Neeraj’));
}
name = ‘Angular’;
}

and now we will have the result that allows us to pass a service from Angular dependency injection successfully

Now let us assume you have some nested dependencies like this:

import{DomSanitizer} from ‘@angular/platform-browser’;
export class GreetingService{
constructor (private domSanitizer:DomSanitizer){

}
sayHello(name){
return Hello ${name}
}
}

In this case, we have to give one more property to the provider’s object (that is deps), which is the array of all the dependencies:

@Component({
selector: ‘my-app’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ],
providers:[{
provide : greetingTokken,
useFactory : greetingFactory,
deps:[DomSanitizer]

}]
})
export class AppComponent {
constructor(@Inject(greetingTokken) private greetingService : GreetingService ){
console.log(this.greetingService.sayHello(‘Neeraj’));
}
name = ‘Angular’;
}

Up until now, all we have done has only been for learning purposes. It is not recommended to create manual providers unless there is a need.

Angular does all the hard work for you. You can reduce the amount of code, and instead of passing the factory and token manually, simply ask the framework to do this for you. The ‘provide’ property, which is the injection token, will be the name of the service which we want to inject, and Angular will internally create an injection token and factory for us.

Now rather than passing the factory and injection token manually,

we will pass the other property (use-class) which tells the framework which class we need to use or inject at run time:

import { Component ,InjectionToken,Inject} from ‘@angular/core’;
import {GreetingService} from ‘./greetingService’;
@Component({
selector: ‘my-app’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ],
providers:[{
provide : GreetingService,
useClass :GreetingService

}]
})
export class AppComponent {
constructor( private greetingService : GreetingService ){
console.log(this.greetingService.sayHello(‘Neeraj’));
}
name = ‘Angular’;
}

Now our code looks much cleaner, and we can further reduce it by just passing the name of the service. Angular will create the provide object, the factory, and the injection token for us and make the instance available to us when needed.

import { Component } from ‘@angular/core’;
import {GreetingService} from ‘./greetingService’;
@Component({
selector: ‘my-app’,
templateUrl: ‘./app.component.html’,
styleUrls: [ ‘./app.component.css’ ],
providers:[GreetingService]
})
export class AppComponent {
constructor( private greetingService : GreetingService ){
console.log(this.greetingService.sayHello(‘Neeraj’));
}
name = ‘Angular’;
}

In the end, our code looks very familiar. Now in the future, whenever you create a service, you know what steps are involved in getting that instance available. 

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Best 50 Angular Interview Questions for Frontend Developers in 2019

How to build a CRUD Web App with Angular 8.0

React vs Angular vs Vue.js by Example

Angular7 CRUD with nodejs and mysql example

Angular 7 + Spring Boot CRUD Example


#angular #javascript #web-development

Learn how Angular’s dependency injection works
29.95 GEEK