Angular Dependency Injection
May 14, 2018
This page will walk through Angular dependency injection example. Dependency injection (DI) is a design pattern where objects are passed to another object to complete the tasks. In angular a service or component may require other dependent services to complete a task. Angular uses dependency injection design pattern to fulfill these dependencies. The advantage of dependency injection design pattern is to divide the task among deferent services. The client service will not create the dependent object itself rather it will be created and injected by an Angular injector. The responsibility of Angular injector is creating service instances and injecting them into classes like components and services. Angular creates root injector during bootstrap process and then creates other injectors. Angular injectors do not know automatically how to create service instances, so we need to specify providers for every service otherwise service instance will not be injected. Injector creates singleton object of a service and hence same object is injected in components and services.
In Angular we specify providers for services using
@Injectable()
, @NgModule()
and @Component()
decorators. Dependency injection in the Angular components and services can be achieved using constructor or Injector
. Now find the complete example of Angular dependency injection step by step.
Contents
1. Technologies Used
Find the technologies being used in our example.1. Angular 6.0.0
2. Angular CLI 6.0.0
3. TypeScript 2.7.2
4. Node.js 10.0.0
5. NPM 6.0.0
2. Specify Providers for Services
As we know that we need to specify providers for services so that Angular injectors can create those service instances. We can specify providers using@Injectable()
, @NgModule()
and @Component()
decorators.
@Injectable(): It has
providedIn
metadata to specify providers for services introduced in Angular 6 version.
@NgModule(): It has
providers
metadata to specify providers for services.
@Component(): It has
providers
metadata to specify providers for services.
Providers use following properties for dependency injection.
useClass: A class provider that creates and returns new instance of specified class.
useExisting: An alias provider that maps one token to another.
useFactory: Configures a factory provider that returns object for dependency injection.
useValue: A value provider that returns a fixed value for dependency injection.
Injector creates singleton object of the class configured by provider for DI. But If we have configured a service in more than one places using provider then object created by injector will be different. Suppose we have two components and they have their respective children components. Suppose both components configure same service class. Now for first component, injector will create a singleton object that will be available for first components and its children components up to the bottom component. For second component, injector will create a different singleton object that will be available for second component and its children components up to the bottom component for DI.
Now we will discuss by code how to use
@Injectable()
, @NgModule()
and @Component()
decorators to specify providers for services.
2.1. Using @Injectable() Providers
@Injectable()
decorator identifies the class or service that is applicable for dependency injection by Angular injectors. @Injectable()
can also specify providers for the service at which it is decorated. @Injectable()
has providedIn
properties using which we can specify provider for that service. Find the code snippet.
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class BookService { ------ }
providedIn: 'root'
means that above service instance will be created by root injector by using service constructor. If there are parameters in constructor, that will be provided by the injector. The service configured with providedIn: 'root'
will be available for dependency injection for all components and services in the application. @Injectable()
decorator can also configure providers using useClass
, useExisting
, useValue
and useFactory
properties. Find some examples.
useExisting
Example:
import { Injectable } from '@angular/core'; import { Computer } from './computer'; import { LaptopService } from './laptop.service'; @Injectable({ providedIn: 'root', useExisting: LaptopService }) export class DesktopService implements Computer { ------ }
useFactory
Example:
import { Injectable } from '@angular/core'; import { BookService } from './book.service' @Injectable({ providedIn: 'root', useFactory: (bookService: BookService) => new PreferredBookService(bookService), deps: [ BookService ] }) export class PreferredBookService { constructor(private bookService: BookService) {} ------ }
2.2. Using @NgModule() Providers
@NgModule()
creates a module and collects module configurations using metadata such as imports
, declarations
, providers
, bootstrap
etc. providers
metadata is used to specify providers for services. Find the example.
@NgModule({ providers: [ BookService, LoggerService ], ------ }) export class AppModule { }
@NgModule()
in AppModule
can be injected in the services and components configured in AppModule
. We can also configure providers using useClass
, useExisting
, useValue
and useFactory
properties with @NgModule()
decorator in providers
metadata.
useClass
Example:
@NgModule({ providers: [ GlobalErrorHandlerService, { provide: ErrorHandler, useClass: GlobalErrorHandlerService } ], ------ }) export class AppModule { }
useValue
Example:
@NgModule({ providers: [ { provide: Book, useValue: JAVA_BOOK }, { provide: HELLO_MESSAGE, useValue: 'Hello World!' } ] ------ }) export class AppModule { }
2.3. Using @Component() Providers
@Component()
decorator creates a class as component. It collects component configurations using metadata such as selector
, providers
, template
etc. providers
metadata is used to specify providers for services. Find the sample example.
@Component({ providers: [ AnimalService, LionService, CowService ], ------ }) export class AnyAnimalComponent { ------ }
@Component()
will be available for this component and its sub-component for dependency injection. We should avoid using providers
metadata configuration in top component that is AppComponent
. Using @Component()
decorator we can also configure providers using useClass
, useExisting
, useValue
and useFactory
properties in providers
metadata.
useValue
Example:
@Component({ providers: [ { provide: Book, useValue: JAVA_BOOK }, { provide: HELLO_MESSAGE, useValue: 'Hello World!' } ], ------ }) export class BookComponent { ------ }
useExisting
Example:
@Component({ providers: [ LaptopService, { provide: DesktopService, useExisting: LaptopService } ], ------ }) export class ComputerComponent { ------ }
3. Inject Services using Constructor
Once we have specified providers for services then we can inject those services into components and other services using constructor. Suppose we have specified providers forBookService
, LoggerService
and UserService
and has declared a component WriterComponent
in @NgModule()
decorator as following.
@NgModule({ providers: [ BookService, LoggerService, UserService ], declarations: [ WriterComponent ] ------ }) export class AppModule { }
BookService
and LoggerService
into UserService
and WriterComponent
using constructor, we will do as following.
Inject
BookService
and LoggerService
into UserService
@Injectable() export class UserService { constructor(private bookService: BookService, private loggerService: LoggerService) { } ------ }
bookService
and loggerService
objects into UserService
.
Inject
BookService
and LoggerService
into WriterComponent
@Component({ selector: 'writer', ------ }) export class ComputerComponent { constructor(private bookService: BookService, private loggerService: LoggerService) { } ------ }
bookService
and loggerService
objects into ComputerComponent
.
4. Inject Services using Injector
AngularInjector
abstract class can be used to inject a service. It is useful in the case when services needs to be instantiated and injected before providers. First we need to inject Injector
into a service using constructor and then using Injector.get
method we can instantiate a service. Suppose we want LoggerService
to inject into GlobalErrorHandlerService
using Injector
. First specify the providers for the services.
import { NgModule, ErrorHandler } from '@angular/core'; @NgModule({ providers: [ LoggerService, GlobalErrorHandlerService, { provide: ErrorHandler, useClass: GlobalErrorHandlerService } ], ------ }) export class AppModule { }
LoggerService
into GlobalErrorHandlerService
using Injector
.
import { Injectable, ErrorHandler, Injector } from '@angular/core'; @Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { const loggerService = this.injector.get(LoggerService); ------ } }
5. InjectionToken, @Inject() and @Optional()
InjectionToken
creates a token that can be used in dependency injection provider specially for string, array, dates, number etc. These values will be injected using @Inject()
decorator in a constructor.
The
@Optional()
decorator makes dependency injection optional. It means if service is not available for injection then null will be assigned. Find the sample example.
import { Component, InjectionToken, Inject } from '@angular/core'; export const HELLO_MESSAGE = new InjectionToken<string>('Hello!'); @Component({ selector: 'book', providers: [ { provide: HELLO_MESSAGE, useValue: 'Hello World!' } ], ------ }) export class BookComponent { constructor( @Inject(HELLO_MESSAGE) private message: string, @Optional() private loggerService: LoggerService ) { } }
message
will be Hello World!
.
If
LoggerService
has not been specified in providers then Angular will not throw error but assign null to loggerService
.
6. Dependency Injection (DI) Complete Example
Now we will provide complete dependency injection (DI) example withuseClass
, useExisting
, useValue
and useFactory
properties. We will use @Injectable()
, @NgModule()
and @Component()
decorators to specify providers for the services in our example. Now find the project structure of our demo application.
my-app | |--src | | | |--app | | | | | |--services | | | | | | | |--animal.service.ts | | | |--lion.service.ts | | | |--cow.service.ts | | | | | | | |--logger.service.ts | | | | | | | |--desktop.service.ts | | | |--laptop.service.ts | | | | | | | |--book.service.ts | | | |--preferred-book.service.ts | | | | | | | |--computer.ts | | | |--book.ts | | | | | |--animal-details.component.ts | | |--any-animal.component.ts | | |--lion.component.ts | | |--cow.component.ts | | | | | |--global-error-handler.service.ts | | | | | |--computer.component.ts | | | | | |--book.component.ts | | | | | |--preferred-book.component.ts | | | | | |--app.component.ts | | |--app.module.ts | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json
6.1. DI with useClass
useClass
is a class provider that creates and returns new instance of specified class. It is used as follows.
providers: [ { provide: AnimalService, useClass: LionService } ]
LionService
is the subclass of AnimalService
. Suppose we have used AnimalService
as DI using constructor in any component or service, by above configuration they will get the object of LionService
as DI.
In case of
useClass
configuration if provider definition is same class as the class used for token given as below.
providers: [ { provide: AnimalService, useClass: AnimalService } ]
providers: [ AnimalService ]
Example 1: Using
@Component()
animal.service.ts
import { Injectable } from '@angular/core'; @Injectable() export class AnimalService { name = 'Animal'; food = 'Food'; getName() { return this.name; } getFood() { return this.food; } }
import { Injectable } from '@angular/core'; import { AnimalService } from './animal.service'; @Injectable() export class LionService extends AnimalService { name = 'Lion'; food = 'Meat'; constructor() { super(); } }
import { Injectable } from '@angular/core'; import { AnimalService } from './animal.service'; @Injectable() export class CowService extends AnimalService { name = 'Cow'; food = 'Grass'; constructor() { super(); } }
import { Component, OnInit } from '@angular/core'; import { AnimalService } from './services/animal.service'; @Component({ selector: 'animal-details', template: ` <h3> {{name}} eats {{food}} </h3> ` }) export class AnimalDetailsComponent implements OnInit { name: string; food: string; constructor(private animalService: AnimalService) { } ngOnInit() { this.name = this.animalService.getName(); this.food = this.animalService.getFood(); } }
import { Component } from '@angular/core'; import { AnimalService } from './services/animal.service'; @Component({ selector: 'any-animal', providers: [ AnimalService ], template: ` <animal-details></animal-details> ` }) export class AnyAnimalComponent { }
import { Component } from '@angular/core'; import { AnimalService } from './services/animal.service'; import { LionService } from './services/lion.service'; @Component({ selector: 'lion', providers: [ { provide: AnimalService, useClass: LionService } ], template: ` <animal-details></animal-details> ` }) export class LionComponent { }
import { Component } from '@angular/core'; import { AnimalService } from './services/animal.service'; import { CowService } from './services/cow.service'; @Component({ selector: 'cow', providers: [ { provide: AnimalService, useClass: CowService } ], template: ` <animal-details></animal-details> ` }) export class CowComponent { }
@NgModule()
global-error-handler.service.ts
import { Injectable, ErrorHandler, Injector } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { LoggerService } from './services/logger.service'; @Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { const loggerService = this.injector.get(LoggerService); if (error instanceof HttpErrorResponse) { loggerService.log('Backend returned status code: ' + error.status); loggerService.log('Response body:' + error.message); } else { loggerService.log('An error occurred:' + error.message); } } }
import { NgModule, ErrorHandler } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { CowComponent } from './cow.component'; import { LionComponent } from './lion.component'; import { AnyAnimalComponent } from './any-animal.component'; import { AnimalDetailsComponent } from './animal-details.component'; import { ComputerComponent } from './computer.component'; import { BookComponent } from './book.component'; import { PreferredBookComponent } from './preferred-book.component'; import { GlobalErrorHandlerService } from './global-error-handler.service'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, CowComponent, LionComponent, AnyAnimalComponent, AnimalDetailsComponent, ComputerComponent, BookComponent, PreferredBookComponent ], providers: [ GlobalErrorHandlerService, { provide: ErrorHandler, useClass: GlobalErrorHandlerService } ], bootstrap: [ AppComponent ] }) export class AppModule { }
6.2. DI with useExisting
useExisting
is an alias provider that maps one token to another. Mapping can be done only if both tokens has same interface. Alias provider is used in the case when we have injected a service in component or in any other service and it has become old and now we want to use new service without changing the code.
Example: Using
@Injectable()
computer.ts
export interface Computer { getComputerName(); }
import { Injectable } from '@angular/core'; import { Computer } from './computer'; @Injectable({ providedIn: 'root' }) export class LaptopService implements Computer { getComputerName() { return 'LAPTOP'; } }
import { Injectable } from '@angular/core'; import { Computer } from './computer'; import { LaptopService } from './laptop.service'; @Injectable({ providedIn: 'root', useExisting: LaptopService }) export class DesktopService implements Computer { getComputerName() { return 'DESKTOP'; } }
DesktopService
, they will use LaptopService
.
computer.component.ts
import { Component, OnInit } from '@angular/core'; import { DesktopService } from './services/desktop.service'; @Component({ selector: 'computer', template: ` <h3> I work on {{computerName}} </h3> ` }) export class ComputerComponent implements OnInit { computerName: string; constructor(private computerService: DesktopService) { } ngOnInit() { this.computerName = this.computerService.getComputerName(); } }
6.3. DI with useValue
useValue
is value provider that returns a fixed value for dependency injection. Injector does not create the object in this case but we create the object and that object is configured with provider using useValue
.
Example: Using
@Component()
book.ts
export class Book { constructor(public name: string, public category: string){} }
import { Component, OnInit, InjectionToken, Inject } from '@angular/core'; import { Book } from './services/book'; const JAVA_BOOK = new Book('Learning Java', 'Java'); export const HELLO_MESSAGE = new InjectionToken<string>('Hello!'); @Component({ selector: 'book', providers: [ { provide: Book, useValue: JAVA_BOOK }, { provide: HELLO_MESSAGE, useValue: 'Hello World!' } ], template: ` <p>Book Name: <b>{{book.name}}</b> </p> <p>Category: <b>{{book.category}}</b></p> <p>Message: <b>{{message}}</b> </p> ` }) export class BookComponent implements OnInit { constructor(private book: Book, @Inject(HELLO_MESSAGE) private message: string) { } ngOnInit() { } }
6.4. DI with useFactory
useFactory
configures a factory provider that returns object for dependency injection.
Example: Using
@Injectable()
book.service.ts
import { Injectable } from '@angular/core'; import { Book } from './book' const BOOKS: Book[] = [ {"name": "Head First Java", "category": "Java"}, {"name": "Hibernate in Action", "category": "Hibernate"}, {"name": "Thinking in Java", "category": "Java"}, {"name": "Beginning Hibernate", "category": "Hibernate"}, {"name": "Effective Java", "category": "Java"}, {"name": "Learning Java", "category": "Java"}, {"name": "Hibernate Recipes", "category": "Hibernate"}, ]; @Injectable({ providedIn: 'root' }) export class BookService { getAllBooks(): Book[] { return BOOKS; } }
import { Injectable } from '@angular/core'; import { BookService } from './book.service' @Injectable({ providedIn: 'root', useFactory: (bookService: BookService) => new PreferredBookService(bookService), deps: [ BookService ] }) export class PreferredBookService { constructor(private bookService: BookService) {} getPreferredBooks() { return this.bookService.getAllBooks() .filter( book => book.category === 'Java') .map(book => book.name) .slice(0, Math.max(0, 3)) .join(' | '); } }
deps
configures the token that will be used by injector to provide dependency injection required by factory method.
preferred-book.component.ts
import { Component, OnInit } from '@angular/core'; import { PreferredBookService } from './services/preferred-book.service'; @Component({ selector: 'preferred-book', template: ` <h3>Preferred Books</h3> {{preferredBooks}} ` }) export class PreferredBookComponent implements OnInit { preferredBooks: string; constructor(private preferredBookService: PreferredBookService) { } ngOnInit() { this.preferredBooks = this.preferredBookService.getPreferredBooks(); } }
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` --- useClass --- <any-animal></any-animal> <lion></lion> <cow></cow> --- useExisting --- <computer></computer> --- useValue --- <book></book> --- useFactory --- <preferred-book></preferred-book> ` }) export class AppComponent { }
7. Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200
Find the print screen of the output.

8. References
Angular Dependency InjectionAngular Providers Example