Angular Providers Example

By Arvind Rai, February 13, 2024
This page will walk through Angular providers example.
1. The Injector injects the objects provided by provider into components and services. Those classes which are decorated with @Injectable() and configured by providers are available for dependency injection (DI).
2. Angular provides different types of providers such as class provider, alias provider, value provider and factory provider. The Injector creates singleton object of a class configured by providers.
3. The providers can be configured at module level as well as component level. If a service class is configured using provider in application module then it will be available for all the components configured in this module. 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.
4. 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.
5. 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.

Injector Providers

A provider provides concrete and runtime version of a dependency value. The Injector injects the objects provided by provider into components and services.

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

2. When providers of @NgModule() decorator configures a service, decorated with @Injectable(), 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. The 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 and 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. The 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()

The Angular @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. The @Injectable() decorator is also responsible to instantiate an Angular component, pipe, directive etc. This becomes possible because @Component, @Pipe and @Directive decorators use @Injectable decorators. 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 following.
@Injectable()
export class AnimalService {

}
@Injectable() is the part of @angular/core API.
If we use providedIn: 'root' in @Injectable() decorator, then we need not to configure this service in providers.
@Injectable({
    providedIn: 'root'
})
export class AnimalService {

}
This service class is available globally to inject it in our application.

InjectionToken

The 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 in constructor as following.
constructor(@Inject(HELLO_MESSAGE) private message: string) { 
} 


Class Provider: useClass

The useClass is a class provider that creates and returns new instance of specified class. It is used as following.
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. The 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 = '';
	food = '';
        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 Providers Example

Alias Provider: useExisting

useExisting is an alias provider that maps one token to another.
providers: [ 
    LaptopService,
    { provide: DesktopService, useExisting: LaptopService }
] 
Mapping can take place 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(): string;
} 
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 = '';
	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 Providers Example

Value Provider: useValue

The 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 following.
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 following.
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(public book: Book, 
	            @Inject(HELLO_MESSAGE) public 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 Providers Example

Factory Provider: useFactory

The useFactory configures a factory provider that returns object for dependency injection. It is used as following.
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 Providers Example

References

Dependency injection in action
Provider

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us