Search code examples
angulardependency-injectionangular-di

Angular dynamic DI with string token


Seems that Angular 6 (4+?) requires token objects to be unique in order for the DI to work. Yet I want to use a dynamic token, to be provided from template html code, which will allow my new directive to resolve a service by name.

Currently the code:

this.service = this.injector.get(new InjectionToken<IServiceRef>(tokenName));

Fails with:

Error: StaticInjectorError(AppModule)[InjectionToken the_token_name]: 

When I replaced my code with the old depricated (in angular 4) Injector.get function, it works okay, because the injector compares names (and I do provide the service in the view by that name...). However with the new DI I am not able to achieve what I want.

So, how to tackle?


Solution

  • You have to use a global storage object for your tokens. I recommend you use a map.

    export const tokens: Map<string, InjectionToken<IServiceRef>> = new Map();
    tokens.set('tokenName', new InjectionToken<IServiceRef>('tokenName'));
    

    You have to use the map object to declare the provider.

    @NgModule({
         providers: [
              {provide: tokens.get('tokenName'), useValue: new Service()}
         ]
     );
    

    You can now look-up the token via the string value.

    this.service = this.injector.get(tokens.get(the_token_name));
    

    I did not know this had changed in Angular 6, but I do recall that the documentation says that tokens are by value reference. Which means that the DI uses === to match dependencies.

    You will get collisions in the DI if you match by token names. Many libraries declare a "document" token as an example. So you don't want to be using string names. As a collision would be extremely difficult to find and fix.