WTH is Injection Token in Angular


Angular is a very advanced and thought-out framework where Angular Components, Directives, Services, and Routes are just the tip of the iceberg. So my intention with WTH-series of posts is to understand something that I do not know yet in Angular (and its brethren) and teach others as well.

Let us start with a simple example, an Angular Injectable Service that we all know and use extensively in any Angular project. This particular example is also very common in Angular projects where we often maintain environment-specific configurations.

// environment.service.ts
import { Injectable } from '@angular/core';

@Injectable({providedIn: 'root'})
export class Environment1 {
    production: boolean = false;
}

Here, we annotated a standard ES6 class with @Injectable decorator and forced Angular to provide it in the application root, making it a singleton service i.e. a single instance will be shared across the application. Then, we can use a Typescript constructor shorthand syntax to inject the above service in Component’s constructor as follows.

// app.component.ts
import { Component } from '@angular/core';

import { Environment1 } from './environment.service'; 

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html'
})
export class AppComponent  {
    constructor(private environment1: Environment1) {}
}

But, often such environment-specific configurations are in the form of POJOs and not the ES6 class.

// environment.service.ts
export interface Environment {
    production: boolean;
}
export const Environment2: Environment = {
    production: false
};

So the Typescript constructor shorthand syntax will not be useful in this case. However, we can naively just store the POJO in a class property and use it in the template.

// app.component.ts
import { Component } from '@angular/core';

import { Environment, Environment2 } from './environment.service'; 

@Component({
    selector: 'my-app'
    templateUrl: './app.component.html'
})
export class AppComponent  {
    environment2: Environment = Environment2;
}

This will work, no doubt!? But, this defeats the whole purpose of Dependency Injection (DI) in Angular which helps us to mock the dependencies seamlessly while testing.

InjectionToken

That’s why Angular provides a mechanism to create an injection token for POJOs to make them injectable.

Creating InjectionToken

Creating an Injection Token is pretty straight-forward. First, describe your injection token and then set the scope with providedIn (just like the Injectable service that we saw earlier) followed by a factory function that will be evaluated upon injecting the generated token in the component.

Here, we are creating an injection token ENVIRONMENT for the Environment2 POJO.

// injection.tokens.ts
import { InjectionToken } from '@angular/core';

import { Environment, Environment2 } from './environment.service'; 

export const ENVIRONMENT = new InjectionToken<Environment>(
  'environment',
  {
    providedIn: 'root',
    factory: () => Environment2
  }
);

Feel free to remove providedIn in case you do not want a singleton instance of the token.

Injecting InjectionToken

Now that we have the Injection Token available, all we need is inject it in our component. For that, we can use @Inject() decorator which simply injects the token from the currently active injectors.

// app.component.ts
import { Component, Inject } from '@angular/core';

import { Environment } from './environment.service'; 
import { ENVIRONMENT } from './injection.tokens';

@Component({
  selector: 'my-app'
  templateUrl: './app.component.html'
})
export class AppComponent  {
  constructor(@Inject(ENVIRONMENT) private environment2: Environment) {}
}

Additionally, you can also provide the Injection Token in @NgModule and get rid of the providedIn and the factory function while creating a new InjectionToken, if that suits you.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { Environment2 } from './environment.service'; 
import { ENVIRONMENT } from './injection.tokens';

@NgModule({
    imports:      [ BrowserModule ],
    declarations: [ AppComponent ],
    bootstrap:    [ AppComponent ],
    providers: [{
        provide: ENVIRONMENT,
        useValue: Environment2
    }]
})
export class AppModule { }

Demo

https://stackblitz.com/edit/angular-2fqggh

That’s it for all. Keep Rocking \m/ \0/

If you found this article useful in anyway, feel free to donate me and receive my dilettante painting as a token of appreciation for your donation.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.