Providers In Angular
In this article, We will learn about Angular Providers using examples. The Angular Dependency Injection system can register classes, functions, or values (dependencies) with Angular Providers. The token is used to register the Providers. To find the supplier, the tokens are used. We can make three different types of tokens. Tokens for type, string, and injection. The Provider additionally instructs the Angular Injector on how to construct the dependent instance. You can create the dependency in four different ways: Class Provider (useClass), Value Provider (useValue), Factory Provider (useFactory), and Aliased Class Provider are the four types of providers ( useExisting).
What are Angular Providers
The Angular Provider is a command (or recipe) that explains how to construct an object for a specific token. An array of such commands can be found in the Angular Providers (Provider). A token (or DI Token) in the Providers Array uniquely identifies each provider.
In the Providers metadata, we record the services that participate in dependency injections. We can accomplish this in two ways.
- Register directly in the @NgModule or @Component's Providers array, or in @Directive.
- Alternatively, use the @Injectable decorator's providedIn property.
For each component/directive it develops, Angular creates an Injector. It also builds an injector at the root level with app-level scope. For Lazy Loaded Modules, it also constructs a Module level Injector.
The Providers are duplicated for each Injector. We can have many providers with the same dependence. The scope of the dependency is determined by where and how the dependency is registered.
In their constructor, Angular Components and Angular Services define the dependencies they require. Using the Token, the Injector reads the dependencies and looks for the provider in the providers array. It then creates the dependency according to the provider's instructions. The dependency's instance is then injected into the Components/Services by the Injector.
Configuring the Angular Provider
To provide a dependent instance, we must first register it in the Providers metadata.
We registered our ProductService using the Providers arrays in the @NgModule in our last Angular Dependency Injection article.
providers: [ProductService]
The above is a shorthand notation for the following that syntax.
providers :[{ provide: ProductService, useClass: ProductService }]
Provide
Provider
DI Token
Type Token
providers :[{ provide: ProductService, useClass: ProductService }]
Using the following code, the ProductService is then injected into the component.
class ProductComponent { constructor(private productService : ProductService ) {} }
String token
Example
{provide:'PRODUCT_SERVICE', useClass: ProductService }, {provide:'USE_FAKE', useValue: true }, {provide:'APIURL', useValue: 'http://SomeEndPoint.com/api' },
Injection Token
export const API_URL= new InjectionToken<string>('');
In the providers array, add the token.
providers: [ { provide: API_URL, useValue: 'http://SomeEndPoint.com/api' } ]
It is then injected into the service/constructor component's using @Inject.
constructor(@Inject(API_URL) private apiURL: string) { }
The Types of Provider
- Class Provider : useClass
- Value Provider: useValue
- Factory Provider: useFactory
- Aliased Class Provider: useExisting
Class Provider: useClass
UseClass Example
providers :[{ provide: ProductService, useClass: ProductService }]
providers: [ProductService]
Switching Dependencies
providers :[{ provide: ProductService, useClass: fakeProductService }]
Value Provider: useValue
UseValue Example
providers :[ {provide:'USE_FAKE', useValue: true}]
Using the @Inject method, we can inject it into the AppComponent.
export class AppComponent { constructor( @Inject('USE_FAKE') public useFake: string ) {}
You have the ability to pass an object. Use Object.freeze to lock the configuration's value so that no one can change it.
const APP_CONFIG = Object.freeze({ serviceURL: 'www.serviceUrl.comapi', IsDevleomentMode: true });
Register it.
providers: [ { provide: 'APP_CONFIG', useValue: APP_CONFIG } ]
Inject it as directed below.
export class AppComponent { constructor( @Inject('APP_CONFIG') public appConfig: any ) {} }
You can also add a feature.
providers: [ { provide: 'FUNC', useValue: () => { return 'hello'; } } ]
The function will be injected exactly as it is. To receive a value from the function someFunc(), you must call it.
export class AppComponent { constructor( @Inject('FUNC') public someFunc: any ) { console.log(someFunc()); } }
Factory Provider: useFactory
UseFactory example
providers: [ { provide: LoggerService, useClass: LoggerService }, { provide: 'USE_FAKE', useValue: true }, { provide: ProductService, useFactory: (USE_FAKE, LoggerService) => USE_FAKE ? new FakeProductService() : new ProductService(LoggerService), deps: ['USE_FAKE', LoggerService] } ]
All of the dependencies of the must be passed as a parameter to the factory function. The injector resolves and injects dependencies using the deps array (third argument).
useFactory: (USE_FAKE, LoggerService)
Depending on the value of USE FAKE, we return FakeProductService or ProductService inside the factory method.
=> USE_FAKE ? new FakeProductService() : new ProductService(LoggerService)
In the final option, we must instruct the Injector on how to inject the Factory function's dependencies. It's worth noting that the order must match that of the factory function argument.
deps: ['USE_FAKE', LoggerService]
The above example code as follows.
export function resolveProductService(USE_FAKE, LoggerService) { return USE_FAKE ? new FakeProductService() : new ProductService(LoggerService); } providers: [ { provide: LoggerService, useClass: LoggerService }, { provide: 'USE_FAKE', useValue: false }, { provide: ProductService, useFactory: resolveProductService, deps: ['USE_FAKE', LoggerService] } ]
useFactory Vs useValue
providers: [ { provide: 'FUNC', useValue: () => { return 'hello'; } } ]
The useValue method returns the function in its original state. To acquire the value, you must invoke the function (someFunc()).
export class AppComponent { constructor( @Inject('FUNC') public someFunc: any ) { console.log(someFunc()); } }
You can achieve the same result by using the useFactory method.
providers: [ { provide: 'FUNC', useFactory: () => { return 'hello'; } } ]
The application of The factory function is called and the result is returned. As a result, you'll get the function's value rather than the function itself in the component.
export class AppComponent { constructor( @Inject('FUNC') public someFunc: any ) { console.log(someFunc); } }
Aliased Provider: useExisting
UseExisting Example
providers: [ { provide: ProductService, useExisting: NewProductService }, { provide: NewProductService, useClass: NewProductService },
providers: [ { provide: ProductService, useExisting: 'PRODUCT_SERVICE' }, { provide: 'PRODUCT_SERVICE', useClass: NewProductService },
Multiple Providers with the same token
@NgModule({ ... providers: [ { provide: ProductService, useClass: ProductService }, { provide: ProductService, useClass: FakeProductService }, ] }) export class AppModule {}
Registering the Dependency at Multiple Providers
@NgModule({ ... providers: [ { provide: ProductService, useClass: ProductService }, ] }) export class AppModule {}
We can also use AppComponent to register it. AppComponent always obtains the dependency registered in the component itself in this situation.
@Component({ selector: 'my-app', templateUrl: './app.component.html', providers: [ProductService] }) export class AppComponent { products: Product[];