An Angular Service provider delivers a runtime version of a dependency value. Therefore, when you inject a service, the Angular injector looks at the providers to create the instance of the service. It is the provider that determines which instance or value should be injected at the runtime in component, pipes, or directives. There are many jargons involved here, so to understand purpose of types of providers, let us start with creating a service. Let’s say we have a service called ErrorService, which is just logging the error message.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
@Injectable() | |
export class ErrorService { | |
logError(message: string) { | |
console.log(message); | |
} | |
} |
Now, we can use this service in a component, as shown in the listing below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component } from '@angular/core'; | |
import { ErrorService } from './errormessage.service'; | |
@Component({ | |
selector: 'app-root', | |
template: ` | |
<input [(ngModel)]='err' type='text'/> | |
<button (click)='setError()'>set error</button> | |
`, | |
providers: [ErrorService] | |
}) | |
export class AppComponent { | |
constructor(private errorservice: ErrorService) {} | |
err: string; | |
setError() { | |
this.errorservice.logError(this.err); | |
} | |
} |
We are importing the service, passing it in the providers array, and injecting it in the constructor of component. We are calling the service method on click of the button, and you can see error message passed in the console. Very simple right?
Here, Angular will rely on the values passed in the providers array of component (or module) to find which instance should be injected at the run time.
“A Provider determines that how object of certain token can be created.”
So, when you pass a service name in providers array of either component or module, like below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
providers: [ErrorService] |
Here, Angular is going to use token value ErrorService and, for token ErrorService, it will create object of ErrorService class. The above syntax is a shortcut of the below syntax:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
providers: [{ | |
provide: ErrorService, useClass: ErrorService | |
}] |
The provide property holds the token that serves as the key for
1. locating the dependency value.
2. registering the dependency.
The second property (it is of four types) is used to create the dependency value. There are four possible values of second parameter, as follows:
1. useClass
2. useExisting
3. useValue
4. useFactory
We just saw example of useClass. Now, consider a scenario that you have a new class for better error logging called NewErrorService.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
@Injectable() | |
export class NewErrorService { | |
logError(message: string) { | |
console.log(message); | |
console.log('logged by DJ'); | |
} | |
} |
useExisting
Now, we want that instead of the instance of ErrorService, the instance of NewErrorService should be injected. Also, ideally, both classes must be implementing the same Interface, which means they will have same method signatures with different implementation. So now, for the token ErrorService, we want the instance of NewErrorService to be injected. It can be done by using useClass, as shown below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
providers: [ | |
NewErrorService, | |
{provide: ErrorService, useClass: NewErrorService} | |
] |
The problem with the above approach is that there will be two instances of NewErrorService. This can be resolved by the use of useExisting.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
providers: [ | |
NewErrorService, | |
{provide: ErrorService, useExisting: NewErrorService} | |
] |
Now there will be only one instance of NewErrorService and for token ErrorService instance of NewErrorService will be created.
Leave a Reply