3

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?

rubmz
  • 1,947
  • 5
  • 27
  • 49
  • But these services are also hardcoded. So why not hardcode your tokens? – Silvermind Oct 25 '18 at 14:52
  • Constant hardcoded strings (which is what I have) are not enough. A single token OBJECT must be use in order for the DI mechanism to work – rubmz Oct 25 '18 at 15:08

1 Answers1

3

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.

Reactgular
  • 52,335
  • 19
  • 158
  • 208
  • Nice solution! I tried it before but I just missed the provide: part... – rubmz Oct 25 '18 at 17:10
  • Awesome solution! Was able to implement a common interface use by multiple service implementation across various modules. @Reactgular, you save my life ;-) – Camille May 13 '21 at 09:11
  • Posted an answer who shows how I solve my problem with your solution [here](https://stackoverflow.com/a/67521314/6796524) – Camille May 13 '21 at 14:53