Injection Token In Angular

Injection Token In Angular

In this article, we will learn about the Injection Token In Angular. Tokens are used by the Angular Dependency Injection framework to uniquely identify a Provider. In Angular, you may construct three different types of tokens. Type Token, String Token, and Injection Token are the three types of tokens.

DI Tokens

With providers metadata, we declare the Provider. This is how it seems.

providers :[{ provide: ProductService, useClass: ProductService }]

There are two properties to the syntax. provider  (ProductService: Provider) & provider (useClass: ProductService)

The Token or DI Token is held by the first property, Provide. The Tokens function as a key. The key is required by the DI systems in order to locate the provider in the Providers array.

A type, a string, or an instance of InjectionToken can be used as the Token.

Type Token

The token is the type that is being injected in this case.

For example, if we want to inject a ProductService instance, we'll use the ProducService as the token, as shown below.
providers :[{ provide: ProductService, useClass: ProductService }]

Using the following code, the ProductService is then injected into the component.
class ProductComponent {
  constructor(private productService : ProductService ) {}
}

You can keep the same token (ProductService) but change the class to a different Product service implementation. For example, we can modify it to BetterProductService in the following code.
providers: [
	{ provide: ProductService, useClass: BetterProductService },

If we use the token again, Angular does not complain. The token ProductService is used twice in the following scenario. In such a case, the last person to register wins (BetterProductService).
providers: [
  { provide: ProductService, useClass: ProductService },
  { provide: ProductService, useClass: BetterProductService }
]

String token

Only if you have Type representation can you use the Type token. However, this is not always the case. In situations when there is no type, we may need to inject simple string values or simple object literals.

In this case, string tokens can be used.

Example
providers: [{ provide: 'PRODUCT_SERVICE', useClass: ProductService }]   

The @Inject function can then be used to inject the ProductService.
export class AppComponent {
  products: Product[];
 
  constructor(
    @Inject('PRODUCT_SERVICE') private productService: ProductService
  ) {}

Example:

Add the following code under the app.module.ts file 
providers: [
    { provide: 'PRODUCT_SERVICE', useClass: const CONFIG = {
  apiUrl: 'http://my.api.com',
  fake: true,
  title: 'Injection Token Example'
};
 
@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent, HelloComponent],
  bootstrap: [AppComponent],
  providers: [
    { provide: 'PRODUCT_SERVICE', useClass: ProductService },
    { provide: 'USE_FAKE', useValue: true },
    { provide: 'APIURL', useValue: 'http://SomeEndPoint.com/api' },
    { provide: 'CONFIG', useValue: CONFIG }
  ]
})
export class AppModule {}

Add the following code under the app.component.ts file
export class AppComponent {
  products: Product[];
 
  constructor(
    @Inject('PRODUCT_SERVICE') private productService: ProductService,
    @Inject('USE_FAKE') public fake: String,
    @Inject('APIURL') public ApiUrl: String,
    @Inject('CONFIG') public Config: any
  ) {}

Problems with the String Tokens

String tokens are simple to use, but they are prone to errors. The same token might be used by two developers in different portions of the program. The same token can be used by third-party libraries.

If we reuse the token, the latest token to be registered overwrites all prior tokens.

String tokens are more easily mistyped, making them difficult to track and maintain in large applications. The InjectionToken enters the scene at this point.

Take a peek at the example app to get a better understanding of the problem. There are two Angular Modules in the app.

One is ProductModule, which implements the ProductService and registers it with the PRODUCT SERVICE string injection token.

SomeOtherModule implements a SomeService and is the second module.
import { Component, Inject } from '@angular/core';
 
//We want Product Service from ProductModule
import { Product, ProductService } from './productmodule/product.service';  
 
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  providers: [],
})
export class AppComponent {
  products: Product[];
 
  constructor(
    @Inject('PRODUCT_SERVICE') private productService: ProductService
  ) {}
 
  getProducts() {
    this.products = this.productService.getProducts();
  }
}

Both modules are imported by AppModule. Which service is used in our component is determined by the import order.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
 
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { ProductModule } from './productmodule/product.module';
import { AnotherModule } from './anothermodule/another.module';
 
@NgModule({
  imports: [BrowserModule, FormsModule, 
             ProductModule, AnotherModule],   //Change the order and you will see different services used in app component
  declarations: [AppComponent, HelloComponent],
  bootstrap: [AppComponent],
})
export class AppModule {}

The code works correctly when ProductModule is placed last in the import array. However, if AnotherModule is imported last, the code fails, and the PRODUCT SERVICE string injection token uses the service from AnotherModule.

What is an Injection Token

The Injection Token lets you to create a token that allows you to inject values that aren't represented at runtime.

String tokens are very similar to this. We generate the Injection Token by creating a new instance of the InjectionToken class, rather than using a hardcoded text. They make certain that the tokens are all one-of-a-kind.

Creating an InjectionToken

We must first import InjectionToken from @angular/core in order to create an Injection Token.
import { InjectionToken } from '@angular/core'; 

From InjectionToken, create a new Injection Token APIURL.
export const APIURL = new InjectionToken<string>('');

It should be added to the providers array.
providers: [ 
{ provide: APIURL, useValue: 'http://SomeEndPoint.com/api' },

It should be injected into the Component.
export class AppComponent {
  constructor(@Inject(APIURL) public ApiUrl: String,) { }
}

InjectionToken Example

The Injection Token is demonstrated in the following example.

Add the following code under the token.ts file.
import { InjectionToken } from '@angular/core';
 
export const APIURL = new InjectionToken<string>('');
export const USE_FAKE = new InjectionToken<string>('');
export const PRODUCT_SERVICE = new InjectionToken<string>('');
export const APP_CONFIG = new InjectionToken<string>('');

Open the app.module.ts file and put the following code 
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
 
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { ProductService } from './product.service';
import { BetterProductService } from './better-product.service';
import { PRODUCT_SERVICE, USE_FAKE, APIURL, APP_CONFIG } from './tokens';
 
const CONFIG = {
  apiUrl: 'http://my.api.com',
  fake: true,
  title: 'Injection Token Example'
};
 
@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent, HelloComponent],
  bootstrap: [AppComponent],
  providers: [
    { provide: PRODUCT_SERVICE, useClass: ProductService },
    { provide: USE_FAKE, useValue: true },
    { provide: APIURL, useValue: 'http://SomeEndPoint.com/api' },
    { provide: APP_CONFIG, useValue: CONFIG }
  ]
})
export class AppModule {}

Now, open the app.component.ts file and put the following code
import { Component, Inject } from '@angular/core';
import { ProductService } from './product.service';
import { Product } from './product';
import { PRODUCT_SERVICE, USE_FAKE, APIURL, APP_CONFIG } from './tokens';
 
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  providers: []
})
export class AppComponent {
  products: Product[];
 
  constructor(
    @Inject(PRODUCT_SERVICE) private productService: ProductService,
    @Inject(USE_FAKE) public fake: String,
    @Inject(APIURL) public ApiUrl: String,
    @Inject(APP_CONFIG) public Config: any
  ) {}
 
  getProducts() {
    this.products = this.productService.getProducts();
  }
}

The Injection token ensures that each token is distinct. Angular DI injection mechanism reliably injects the right dependency even if the two libraries have the same name.

Conclusion

In this article, We covered the Injection Token In Angular. Tokens are used by the Angular Dependency Injection framework to uniquely identify a Provider.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in comment section.

Post a Comment

Previous Post Next Post