Home  >  Angular

Angular 2/4 Providers Example

By Arvind Rai, August 18, 2017
This page will walk through Angular providers example. Injector injects the objects provided by provider into components and services. Only those classes which are configured by providers are available for dependency injection (DI). Angular provides different types of providers such as class provider, alias provider, value provider and factory provider. Injector creates singleton object of a class configured by providers. Providers can be configured at module level as well component level. If a service class is configured using provider in module then it will be available for all the components configured in module for dependency injection. If a service is configured using provider in component then it will be available in current component and its children component up to the bottom component. In our application if we have configured a service using providers in root component then it will be available everywhere in the application components and services for dependency injection. It is better to configure service using provider only in that component that itself and its children up to the bottom component can use the service for dependency injection.
Generally we configure a service using providers as given below.
providers: [ 
    AnimalService
] 
That is the short form of following configuration.
providers: [ 
    { provide: AnimalService, useClass: AnimalService }
]
On this page we will create examples for class provider, alias provider, value provider and factory provider. Now find the complete example step by step.

Technologies Used

Find the technologies being used in our example.
1. Angular 4.2.0
2. TypeScript 2.3.3
3. Node.js 6.10.1
4. Angular CLI 1.2.0
5. Angular Compiler CLI 4.2.0

Injector Providers

A provider provides concrete and runtime version of a dependency value. Injector injects the objects provided by provider into components and services. So it is necessary to configure a service with provider otherwise injector will not be able to inject it.

1. Provider is configured using providers metadata. providers metadata is available in @NgModule() decorator as well as in @Component() decorator.

2. When providers of @NgModule() decorator configures a service in application module then that service will be available for dependency injection in all those components which are configured in declarations metadata of that @NgModule() decorator.

3. When providers of @Component() decorator configures a service in a component then that service will be available for dependency injection in current component and its children components up to the bottom component.

4. 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.

5. providers metadata has two parts i.e. token and provider definition object. Token is configured by provide attribute and provider definition object is configured by useClass, useExisting, useValue and useFactory.

6. We configure providers as following.

a. Using useClass for class provider.
providers: [ 
   { provide: AnimalService, useClass: LionService }
]
b. Using useExisting for alias provider.
providers: [ 
   { provide: DesktopService, useExisting: LaptopService }
]
c. Using useValue for value provider.
providers: [ 
   { provide: HELLO_MESSAGE, useValue: 'Hello World!' }
]
d. Using useFactory for factory provider.
providers: [ 
   { provide: PREFERRED_BOOKS, useFactory: preferredBooksFactory(3), deps: [Book, BookService] }		
]

All above configurations contain two parts:
1. Token: First part is token configured by provide attribute.
2. Provider Definition Object: Second part is provider definition object configured by useClass, useExisting, useValue. In case of useFactory, it is factory method that returns object, string, array etc for dependency injection.

@Injectable()

@Injectable() decorator is a marker used at class level. It tells Injector that this class is available for creation by Injector. We use @Injectable() in our service class so that the service object can be created automatically for dependency injection in any component or any other service class. @Injectable() is also responsible to instantiate an angular component, pipe, directive etc. This becomes possible because @Component, @Pipe and @Directive decorators use @Injectable decorator. If our service class decorated with @Injectable() has dependency of other service classes, then we need not to worry for its creation and dependency injection will be performed by Injector automatically.
If our service class has no dependency of other service classes, then it is not necessary to decorate our service class with @Injectable() decorator. Still the object of this class can be created by Injector for dependency injection. But it is better to use @Injectable() decorator in this case also because in future our service class may need the dependency of other service classes. At that time Injector will throw error for our service class if not decorated with @Injectable(). So it is better to use @Injectable() decorator always at our service class. A class decorated with @Injectable() is created as follows.
@Injectable()
export class AnimalService {

}
@Injectable() is the part of @angular/core API.

InjectionToken

InjectionToken creates a token that can be used in dependency injection (DI) provider. If we want to inject any simple data such as string, array, dates, number etc then for their dependency injection we need to create an instance of InjectionToken as following.
export const HELLO_MESSAGE = new InjectionToken<string>('Hello!'); 
Now use the above constant in providers.
providers: [ 
    { provide: HELLO_MESSAGE, useValue: 'Hello World!' }
]
When we perform dependency injection of HELLO_MESSAGE using constructor in any component or service, we get its value as Hello World!. We can inject string, array, dates, number etc with the help of @Inject() decorator using constructor as following.
constructor(@Inject(HELLO_MESSAGE) private message: string) { 
}

Class Provider: 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 }
] 
In the above configuration first part is token and second part is provider definition object. The class type of provider definition object must be same as token class or its subclass. 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 }
] 
Then service can be configured by provider in short as following.
providers: [ 
    AnimalService
] 
Now we will create an example.
animal.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class AnimalService {
	name = 'Animal';
	food = 'Food';	
	getName() {
		return this.name;
	}
        getFood() {
		return this.food;
	}		
} 
cow.service.ts
import { Injectable } from '@angular/core';
import { AnimalService } from './animal.service';

@Injectable()
export class CowService extends AnimalService {
	name = 'Cow';
	food = 'Grass';	
} 
lion.service.ts
import { Injectable } from '@angular/core';
import { AnimalService } from './animal.service';

@Injectable()
export class LionService extends AnimalService {
	name = 'Lion';
	food = 'Meat';
} 
animal-details.component.ts
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();
	}	
} 
cow.component.ts
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 {
} 
lion.component.ts
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 {
} 
any-animal.component.ts
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 {
} 
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
          <any-animal></any-animal>
          <lion></lion> 
	  <cow></cow>
  `
})
export class AppComponent {
} 
app.module.ts
import { NgModule } 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';

@NgModule({
  imports: [     
        BrowserModule
  ],
  declarations: [
        AppComponent,
	CowComponent,
	LionComponent,
	AnyAnimalComponent,
	AnimalDetailsComponent
  ],
  providers: [ 
  ],  
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 
Find the print screen of the output.
Angular 2/4 Providers Example

Alias Provider: useExisting

useExisting is an alias provider that maps one token to another.
providers: [ 
    LaptopService,
    { provide: DesktopService, useExisting: LaptopService }
] 
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. By above configuration wherever we have injected DesktopService, now they will use LaptopService. Find the example.
computer.ts
export interface Computer {
    getComputerName();
} 
desktop.service.ts
import { Injectable } from '@angular/core';
import { Computer } from './computer';

@Injectable()
export class DesktopService implements Computer {
	getComputerName() {
		return 'DESKTOP';
	}
} 
laptop.service.ts
import { Injectable } from '@angular/core';
import { Computer } from './computer';

@Injectable()
export class LaptopService implements Computer {
	getComputerName() {
	   return 'LAPTOP';
	}	
} 
computer.component.ts
import { Component, OnInit } from '@angular/core';
import { DesktopService } from './services/desktop.service';
import { LaptopService } from './services/laptop.service';

@Component({
    selector: 'computer',
    providers: [ 
	    LaptopService,
	    { provide: DesktopService, useExisting: LaptopService }
    ],     
    template: `
	     <h3> I work on {{computerName}} </h3>
	`
})
export class ComputerComponent implements OnInit {
	computerName: string;
	constructor(private computerService: DesktopService) { }
	ngOnInit() {
		this.computerName = this.computerService.getComputerName();
	}
} 
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
          <computer></computer>
  `
})
export class AppComponent {
} 
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { ComputerComponent }  from './computer.component';

@NgModule({
  imports: [     
        BrowserModule
  ],
  declarations: [
        AppComponent,
	ComputerComponent,
  ],
  providers: [ 
  ],  
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 
Find the print screen of the output.
Angular 2/4 Providers Example

Value Provider: 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. It is used as follows.
Find the two fixed values.
const JAVA_BOOK = new Book('Learning Java', 'Java');
export const HELLO_MESSAGE = new InjectionToken('Hello!'); 
Now they will be configured as follows.
providers: [ 
    { provide: Book, useValue: JAVA_BOOK },
    { provide: HELLO_MESSAGE, useValue: 'Hello World!' }
] 
If Book has been injected in any component or service using constructor then the injector will inject fixed value JAVA_BOOK there. In the same way wherever HELLO_MESSAGE are injected, they will get Hello World! value. Now find the example.
book.ts
export class Book {
	constructor(public name: string, public category: string){}
} 
book.component.ts
import { Component, OnInit, InjectionToken, Inject } from '@angular/core';
import { Book } from './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() {
	}
} 
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
          <book></book>
  `
})
export class AppComponent {
} 
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { BookComponent }  from './book.component';

@NgModule({
  imports: [     
        BrowserModule
  ],
  declarations: [
        AppComponent,
	BookComponent,
  ],
  providers: [ 
  ],  
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 
Find the print screen of the output.
Angular 2/4 Providers Example

Factory Provider: useFactory

useFactory configures a factory provider that returns object for dependency injection. It is used as follows.
providers: [ 
    BookService,
    { provide: Book, useValue: JAVA_BOOK },
    { provide: PREFERRED_BOOKS, useFactory: preferredBooksFactory(3), deps: [Book, BookService] }
] 
Find attribute definition used in above code snippet.
provide: Configures the token that will be used in dependency injection.
useFactory: Configures a factory method that can return object, string, array etc.
deps: Configures the token that will be used by injector to provide dependency injection required by factory method.

Now find the example.
book.component.ts
import { Component, OnInit, InjectionToken, Inject } from '@angular/core';
import { Book } from './book';
import { BookService } from './book.service';
import { PREFERRED_BOOKS, preferredBooksFactory } from './preferred-books'

const JAVA_BOOK = new Book('Thinking in Java', 'Java');

@Component({
    selector: 'book',
    providers: [ 
	BookService,
	{ provide: Book, useValue: JAVA_BOOK },
        { provide: PREFERRED_BOOKS, useFactory: preferredBooksFactory(3), deps: [Book, BookService] }
    ],     
    template: `
	  <h3>Preferred Books</h3>
	  {{preferredBooks}}
	`
})
export class BookComponent implements OnInit {
	constructor(@Inject(PREFERRED_BOOKS) private preferredBooks: string) { }
	
	ngOnInit() {
	}
} 
book.ts
export class Book {
	constructor(public name: string, public category: string){}
} 
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()
export class BookService {
	getAllBooks(): Book[] {
		return BOOKS;
	}
} 
preferred-books.ts
import { InjectionToken } from '@angular/core';
import { Book }        from './book';
import { BookService } from './book.service';

export const PREFERRED_BOOKS = new InjectionToken<string>('book name');

export function preferredBooksFactory(count: number) {
  return (myBook: Book, bookService: BookService): string => {
    return bookService
        .getAllBooks()
        .filter( book => book.category === myBook.category)
        .map(book => book.name)
        .slice(0, Math.max(0, count))
	.join(' | ');
  };
}; 
The dependency injection of Book and BookService arguments in factory method is picked up by injector from deps configuration given below.
{ provide: PREFERRED_BOOKS, useFactory: preferredBooksFactory(3), deps: [Book, BookService] } 
The value passed in preferredBooksFactory() as argument is the count of books returned by our factory method.
app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
          <book></book>
  `
})
export class AppComponent {
} 
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { BookComponent }  from './book.component';

@NgModule({
  imports: [     
        BrowserModule
  ],
  declarations: [
        AppComponent,
	BookComponent,
  ],
  providers: [ 
  ],  
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 
Find the print screen of the output.
Angular 2/4 Providers Example

Run Demo Application

To run the demo application, find following 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. Now access the URL http://localhost:4200

I am done now. Happy Angular 2+ Learning!

References

Dependency Injection
DI in Action

Download Demo Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
FIND MORE TUTORILAS


©2018 concretepage.com | Privacy Policy | Contact Us