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?
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.