Injection tokens in Angular

Dependency Injection Tokens

Dependency Injection

Dependency Injection (DI) is a design pattern for creating and delivering required dependencies from the external source rather than being created and managed by the dependent component. The Dependency Injection mechanism support is one of the core features of Angular as a framework.

In Angular, dependencies are typically services, but they also can be values, such as strings or functions.

  @Injectable()
  class SomeService {}
  @Component({
    // ...
    providers: [SomeService]
  })
  class ExampleComponent {
    private service = inject(SomeService); // or via constructor
  }

Dependency Injection Tokens

DI Tokens are keys used by Angular to check which instance should be resolved at runtime.

Type token

The most commonly used way to register providers (especially for services) is using type as a token.

  providers: [
    SomeService
  ]

If we want to manipulate which instance is provided we can use properties useClass, useFactory or useExisting in provider definition.

  providers: [
    { provide: SomeService, useClass: BetterService} // or by: useValue, useFactory, useExisting
  ]

When we use sam token twice - last registered provider will be in use.

String token

In case when we haven't got defined type and want to inject plain object or value we can use string tokens.

  providers: [
    { provide: 'API_URL', useValue: 'http://localhost:8080'},
  ]

Now we can inject this value into the constructor using @Inject() parameter decorator (or via inject method).

  @Component({
  // ...
  })
  class ExampleComponent {
    constructor(@Inject('APIURL') public apiUrl: string) {}
  }

String tokens may lead to problems when there are multiple different tokens with shared name defined in separate modules. If we try to use such token with multiple definitions in module then the order of imports determines which value will be provided with it (again - last imported module).

Injection token

Instead of using hardcoded string we can create token with generic class InjectionToken<T>. Using it not only enables static type checks but more importantly it ensures uniqueness of the tokens.

  import { InjectionToken } from '@angular/core';
  
  export const API_URL = new InjectionToken<string>(
    'api.url', // token description
    {
      factory: () => 'http://localhost:8080' // default value
    }
  );
  providers: [
    { provide: API_URL, useValue: 'http://localhost:8081'},
  ]
  @Component({
    // ...
  })
  class ExampleComponent {
    private url = inject(API_URL);
  }