Stop being scared of InjectionTokens
Tags: angular
Published: 2023-02-02 11:25:00

Injection Tokens seem to intimidate many developers. They don't understand what they are, how to use them, and their purpose.

To overcome the fear of this feature, it is important to have a basic understanding of how Angular's Dependency Injection works. _(A separate article will dive deeper into the inner workings of Angular's DI system for a full understanding)_.

Here's an example of how the singleton pattern operates. When we add `@Injectable({providedIn: 'root'})` to one of our services:

```typescript
@Injectable({ providedIn: 'root' })
export class MyService {
 // ...
}
```
The first time, we will call `MyService` Angular will store the service in a `record` of the `RootInjector` with the key being `class MyService` and the value being an object containing a factory of `MyService` , the current value and a `multi` flag.

```typescript
record:{
 //...
 [index]:{
   key: class MyService,
   value: {
    factory: f MyService_Factory(t),
    multi: undefined
    value: {}
   }
 //...
}
```
This way, the next time we want to inject our service through the constructor or the `inject` function in another Component, Angular's DI will look for the service in the `record` object and return its current value or create it using the factory function.

---

It's also crucial to understand what happens when we provide our service directly in the `bootstrapApplication` or Component provider array.

To inject MyService we write the following:

```typescript
@Component({
  // ...
  providers: [MyService],
})
export class AppComponent {}
```
This code is a shorthand syntax for

```typescript
@Component({
  // ...
  providers: [{provide: MyService, useClass: MyService}],
})
export class AppComponent {}
```

---

By creating an InjectionToken, we are creating a "static" key for our `record` or `dictionary` of services.

By writing this:

```typescript
export const DATA = new InjectionToken<string>('data');
```
We are creating a "constant" of type `string`.

The **InjectionToken** can take an object as second parameter containing a factory function and a `providedIn` attribut _(Although this parameter will be deprecated as the default value is root and there is no other possibility)_. The factory function acts like a default value if no other value is provided.

Let's take a look at the following example to clarify it.

```typescript
export const DATA = new InjectionToken<string>('data', {
  factory: () => 'toto',
});
```
Angular will store our token inside the `record` object of the `RootInjector`:

```typescript
record: {
 //...
 [index]: {
   key: InjectionToken {_desc: 'data', ngMetadataName: 'InjectionToken'}
   value: {
     factory: () => 'toto',
     multi: undefined,
     value: "toto"
   }
 },
 //...
}
```
Then when we inject it inside a Component, Angular's DI will look for `DATA` key in the `RootInjector`'s `record` and inject the corresponding value. ('toto' is this example)

```typescript
@Component({
  // ...
  template: `{{ data }} `, // toto
})
export class AppComponent {
  data = inject(DATA);
}
```
However, in a Component provider array, we can override this Token to map `DATA` with another value. This time Angular will store the value inside the `record` of the `NodeInjector`.

```typescript
@Component({
  // ...
  provider: [{provide: DATA, useValue: 'titi'}],
  template: `{{ data }} `, // titi
})
export class AppComponent {
  data = inject(DATA);
}
```
When Angular searches for the `DATA` token, it first examines the component's `NodeInjector`, then the `NodeInjector` of its parent and finally the `RootInjector`. As a result, in this example, the output value will be _'titi'_.

## Providing your InjectionToken

### useValue
The `useValue` keyword allows us to supply a string, number, interface, class instance or any constant value. It is very useful for setting up configuration properties.

```typescript
export type Environment = 'local' | 'dev' | 'int' | 'prod';

export interface AppConfig {
  version: string;
  apiUrl: string;
  environment: Environment;
}

export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');

export const getAppConfigProvider = (value: AppConfig): ValueProvider => ({
  provide: APP_CONFIG,
  useValue: value,
});

bootstrapApplication(AppComponent, {
  providers: [
    getAppConfigProvider(environment) // environment files
  ],
});
```
In the example above, we are creating an **InjectionToken** to store our environment configuration. 

> This is the recommended method for accessing environment variables in an Nx workspace, as it avoids circular dependencies.
To retrieve our environment properties, we inject the token inside our component.

```typescript
@Component({
  selector: 'app-root',
  standalone: true,
  template: `{{ config.version }}`,
})
export class AppComponent {
  config = inject(APP_CONFIG);
}
```

### useClass
The `useClass` keyword allows us to instantiate a new class. This is useful for implementing the dependency inversion principle for instance.

The dependency inversion principle is a software design principle that states that different layers depend on abstractions, rather than on each other. This inversion makes the code more flexible, maintainable and more testable.

```typescript
export interface Search<T> {
  search: (search: string) => T[];
}

export const SEARCH = new InjectionToken<Search<object>>('search');

@Component({
  selector: 'shareable',
  standalone: true,
  imports: [ReactiveFormsModule, NgFor, AsyncPipe, JsonPipe],
  template: `
    <input type="text" [formControl]="searchInput" />
    <button (click)="search()"></button>
    <div *ngFor="let d of data | async">{{ d | json }}</div>
  `,
})
export class ShareableComponent {
  searchService = inject(SEARCH, {optional: true}); 
  data = new BehaviorSubject<object[]>([]);

  searchInput = new FormControl('', { nonNullable: true });

  // We are not injecting `searchService` with the constructor, 
  // because `inject` function infers the type. 
  constructor() {
    if (!this.searchService) 
       throw new Error(`SEARCH TOKEN must be PROVIDED`);
  }

  search() {
    this.data.next(this.searchService.search(this.searchInput.value));
  }
}
```
The `ShareableComponent` displays an input field, a search button and a list of the search results. When we hit the search button, the component searches "somewhere" and displays the results. This component is highly generic and does not require any additional information about how or where to search. Implementation details are provided by the parent component.

Let's see how we can utilize the `ShareableComponent`.

First, we will create an utility function to provide our Token. This creates a better developer experience and reduces the likelihood of errors as it adds strong typing to our function.

```typescript
export const getSearchServiceProvider = <T, C extends Search<T>>(clazz: new () => C): ClassProvider => ({
  provide: SEARCH,
  useClass: clazz,
});
```
Using the above function, we can supply the desire implementation of the `Search` interface within the component that calls the `ShareableComponent`. When Angular attempts to inject the `SEARCH` **InjectionToken** , it will traverse each `NodeInjector` up the component tree until it finds the provided implementation. 

```typescript
@Injectable()
export class DetailSearchService implements Search<Detail> {
  search = (search: string): Detail[] => {
  // implementation of our search function
  }
}

@Component({
  selector: 'parent',
  standalone: true,
  imports: [ShareableComponent],
  providers: [getSearchServiceProvider(DetailSearchService)],
  template: `
   <shareable></shareable>
  `,
})
export class ParentComponent {}
```
If we want to reuse `ShareableComponent` with a different implementation of search, this becomes very easy. 

### useFactory
The `useFactory` keyword allows us to provide an object though a factory function.

```typescript
export const USER = new InjectionToken<string>('user');

export const getUserProvider = (index: number): FactoryProvider => ({
  provide: USER,
  useFactory: () => inject(Store).select(selectUser(index)),
});
```
The `inject` function enables us to incorporate other services within the factory function.

Alternatively, if you do not wish to use the `inject` function, the `FactoryProvider` has a third parameter named `deps` where you can inject other injectable services.

```typescript
export const getUserProvider = (index: number): FactoryProvider => ({
  provide: USER,
  useFactory: (store: Store) => store.select(selectUser(index))],
  deps: [Store],
});
```
But I advice you to use the `inject` function. It's cleaner and easier to understand.

We can then use this provider as follow to retrieve one user from the store:

```typescript
@Component({
  selector: 'app-root',
  standalone: true,
  providers: [getUserProvider(2)],
  template: ` <div>{{ user }}</div> `,
})
export class ParentComponent {
  user = inject(USER);
}
```

### useExisting
The `useExisting` keyword allows us to map an existing instance of a service to a new token, creating an alias.

When examining the `record` object created by Angular, we have the following: 

```typescript
record:{
 //...
 [index]:{
   key: InjectionToken {_desc: 'data', ngMetadataName: 'InjectionToken', ɵprov: undefined},
   value: {
    factory: () => ɵɵinject(resolveForwardRef(provider.useExisting)),
    multi: undefined
    value: {}
   }
 //...
}
```
Angular resolves the InjectionToken by forwarding the reference to the class specified in the `useExisting` parameter. It searches the `NodeInjector` and `RootInjector` for the class, so it must be set beforehand or an error will occur.

```bash
NullInjectorError: No provider for XXX!
```

---

### Notes

When providing an InjectionToken through the providers, there is not type safety. For example, the following code would not produce a Typescript error, even though the type is inferred as `number` for `num` , but a `string` is provided. _(We might have an error at runtime.)_
export const NUMBER = new InjectionToken<number>('number');

```typescript
@Component({
  providers: [{provide: NUMBER, useValue: 'toto'}],
  // ... 
})
export class ParentComponent {
  num = inject(NUMBER);
  //^ (property) ParentComponent.num: number
}
```
To ensure type safety, I advise to create a utility function to provide your token.

```typescript
export const NUMBER = new InjectionToken<number>('number');

export const getNumberProvider = (num: number): ValueProvider => ({
  provide: NUMBER,
  useValue: num
});
// NUMBER token should be provided through this function

@Component({
  providers: [getNumberProvider('toto')],
                                 //^ Argument of type 'string' is not assignable to parameter of type 'number'
  // ...
})
export class ParentComponent {
  num = inject(NUMBER);
}
```
This way, our implementation is safe and any errors will occur at compile time, rather than at runtime.

---

That's it for this article! You should now have a good understanding of InjectionToken and what they do mean. I hope that this information has demystified them for you and you will no longer feel intimidated by them.

I hope you learned new Angular concept. If you liked it, you can find me on [Twitter](https://twitter.com/laforge_toma) or [Github](https://github.com/tomalaforge).

> 👉 If you want to accelerate your Angular and Nx learning journey, come and check out [Angular challenges](https://github.com/tomalaforge/angular-challenges).