Dependency injection (DI) is a core concept in Angular, allowing us to efficiently manage and share instances of services and values throughout our application. While DI often involves the injection of classes, there are cases where you need to inject non-class dependencies or provide multiple instances of the same type with different configurations. This is where InjectionToken
comes into play.
Understanding InjectionToken
InjectionToken
is a powerful feature in Angular that allows us to create a unique token that can be used as a key when defining dependencies for injection. It’s particularly useful when:
- You need to inject non-class dependencies, such as configuration values, API endpoints, or string constants.
- You want to provide multiple instances of the same type with different configurations.
In this blog post, we’ll explore both scenarios with practical examples to illustrate the power and versatility of InjectionToken
.
Scenario 1: Providing a Configuration Value
Imagine you’re developing an Angular application, and you need to provide a configuration value that can be accessed throughout your app. Here’s how you can use InjectionToken
for this purpose:
Step 1: Define an InjectionToken
import { InjectionToken } from '@angular/core';
// Create an InjectionToken for the configuration value
export const APP_CONFIG = new InjectionToken<string>('app.config');
In this step, we’ve defined an InjectionToken
named APP_CONFIG
that will represent our configuration value.
Step 2: Provide the Configuration Value
Next, let’s provide the actual configuration value in your Angular module:
import { NgModule } from '@angular/core';
import { APP_CONFIG } from './app-config.token';
@NgModule({
providers: [
{ provide: APP_CONFIG, useValue: 'codeguru' },
],
})
export class AppModule {}
In the above code, we use the provide
key to associate our APP_CONFIG
token with the value 'codeguru'
. You can replace this value with the actual configuration you want to use in your application.
Step 3: Inject and Use the Configuration Value
With the configuration value provided, you can now inject and use it in your components or services:
import { Component, Inject } from '@angular/core';
import { APP_CONFIG } from './app-config.token';
@Component({
selector: 'app-root',
template: `
<div>
Configuration Value: {{ configValue }}
</div>
`,
})
export class AppComponent {
constructor(@Inject(APP_CONFIG) public configValue: string) {}
}
In the AppComponent
, we inject the APP_CONFIG
token using Angular’s @Inject
decorator and access the configuration value as configValue
.
Scenario 2: Providing Multiple Instances
Another common use case for InjectionToken
is providing multiple instances of the same type with different configurations. Here’s an example:
Suppose you have two different API endpoints for user data, one for production and one for development. You want to inject the appropriate API endpoint based on the environment.
Step 1: Define InjectionTokens
api-endpoints.tokens
import { InjectionToken } from '@angular/core';
// Create InjectionTokens for the API endpoints
export const PROD_API_ENDPOINT = new InjectionToken<string>('prod.api.endpoint');
export const DEV_API_ENDPOINT = new InjectionToken<string>('dev.api.endpoint');
In this step, we’ve created two InjectionTokens
, PROD_API_ENDPOINT
and DEV_API_ENDPOINT
, each representing a different API endpoint.
Step 2: Provide the API Endpoints
Now, let’s provide the actual API endpoints in your Angular module, based on your environment:
import { NgModule } from '@angular/core';
import { PROD_API_ENDPOINT, DEV_API_ENDPOINT } from './api-endpoints.tokens';
@NgModule({
providers: [
{ provide: PROD_API_ENDPOINT, useValue: 'https://api.example.com' },
{ provide: DEV_API_ENDPOINT, useValue: 'https://dev-api.example.com' },
],
})
export class AppModule {}
Here, we’ve provided the production and development API endpoints using the corresponding InjectionToken
.
Step 3: Inject and Use the API Endpoint
Now, you can inject and use the appropriate API endpoint in your service:
import { Injectable, Inject } from '@angular/core';
import { PROD_API_ENDPOINT, DEV_API_ENDPOINT } from './api-endpoints.tokens';
@Injectable()
export class ApiService {
constructor( @Inject(PROD_API_ENDPOINT) private prodApiEndpoint: string,
@Inject(DEV_API_ENDPOINT) private devApiEndpoint: string ) {}
getApiEndpoint(isDev: boolean): string {
return isDev ? this.devApiEndpoint : this.prodApiEndpoint;
}
}
In the ApiService
, we inject both PROD_API_ENDPOINT
and DEV_API_ENDPOINT
and use a logic (not shown here) to determine which one to use based on the environment.
app.componennt.ts
import { Component, Inject, OnDestroy } from '@angular/core';
import { ApiService } from './api.service';
@Component({
selector: 'my-app',
template: `
<div>
EndPoint Value: {{ endPoint }}
</div>
`,
providers: [ApiService],
})
export class AppComponent {
endPoint: string;
constructor(private apiSvc: ApiService) {
this.endPoint = this.apiSvc.getApiEndpoint(true);
}
}
Demo
InjectionToken
is a powerful tool in Angular’s dependency injection system. It enables you to inject non-class dependencies and provide multiple instances of the same type with different configurations. By following the examples provided in this blog post, you can harness the flexibility and versatility of InjectionToken
in your Angular applications, making your code more maintainable and adaptable to various scenarios.