Angular 5 Injector - How to inject string

11,356

Solution 1

get is deprecated: from v4.0.0 use Type or InjectionToken

warning refers to the fact that injector.get is generic method and expects type argument. This is exactly what Injector documentation states:

get(token: Type | InjectionToken, notFoundValue?: T): T

Considering that name string token is supposed to resolve to a string, it should be:

this.injector.get<string>(<any>'name');

String tokens are deprecated as well, and a token is expected to be either a function or InjectionToken instance.

however I don't want to useValue or useClass, what I need is the actual value injected.

All dependencies should be registered as module or component providers in order to be injected.

Solution 2

First you need to create an InjectionToken (here in values.ts):

import { InjectionToken } from '@angular/core';

export const PAGE_TITLE = new InjectionToken<string>('page.title');

Then you need to import it into your module and provide a value when that token is requested:

providers: [ HeroService, MessageService,
    { provide: PAGE_TITLE, useValue: "My Wonderful Title" }
],

Then you need to import that token where you want to inject the value and use @Inject():

constructor(@Inject(PAGE_TITLE) title) {
  this.title = title;
}

Sample on Stackblitz

Looking at ReflectiveInjector it says it is slow and to use Injector.create instead btw...

Share:
11,356
Royalsmed
Author by

Royalsmed

Updated on June 11, 2022

Comments

  • Royalsmed
    Royalsmed almost 2 years

    I am trying to inject strings into my angular component. The code below works fine but it gives a deprecation warning: get is deprecated: from v4.0.0 use Type or InjectionToken

    @Component({
      selector: 'app-label',
      templateUrl: './label.component.html',
      styleUrls: ['./label.component.scss']
    })
    export class LabelComponent {
      public name: string;
      public caption: string;
      constructor(
        private injector: Injector
      ) {
        this.name = this.injector.get('name');
        this.caption = this.injector.get('caption');
      }
    }
    

    So i tried to work with this very short and incomplete documentation Angular 5 Injector to come up with something like below, however I don't want to useValue or useClass, what I need is the actual value injected.

        class BS { }
        // [..]
        const name = new InjectionToken<string>('name');
        const caption = new InjectionToken<string>('caption');
        const injector = ReflectiveInjector.resolveAndCreate([
          { provide: name, useClass: BS},
          { provide: caption, useClass: BS}
        ]);
        this.name = this.injector.get(name);
        this.caption = this.injector.get(caption);
        console.log(this.name); // will be an instance of BS
    

    I am really stuck here and the documentation is not helping at all.

    I need this for Dynamic component Loading and Injection. Full Plunker can be found in: Plunker Dynamic Component Loading and Injection

  • Royalsmed
    Royalsmed about 6 years
    That's not what I am looking for actually, I could achieve the same with the code above and useValue { provide: name, useValue: 'bs1'}, { provide: caption, useValue: 'bs2'} The injected values are dynamic and coming from another service.
  • Jason Goemaat
    Jason Goemaat about 6 years
    I'm don't understand, if the values are being provided by a service, shouldn't the service be injected? Are you trying to create a service that ties into the injection at run-time and dynamically alters the values that will be injected when new instances are created?
  • Estus Flask
    Estus Flask about 6 years
    @Royalsmed Then the question is XY problem. Generally you don't need to use ReflectiveInjector at all for regular tasks. If you need it, you can be sure that you're doing something wrong.
  • Royalsmed
    Royalsmed about 6 years
    unfortunately this is not how it works. this.injector.get<string>('name'); won't work because Argument of type '"name"' is not assignable to parameter of type 'InjectionToken<string> | Type<string>'.
  • Royalsmed
    Royalsmed about 6 years
    @estus I don't think I am doing something wrong but I might be wrong :p here is a full Plunker with what I am trying to achieve link
  • Estus Flask
    Estus Flask about 6 years
    I updated the answer with explanation. String tokens are deprecated, and you have to cheat it with <any>. In your case embed.plnkr.co/p7VHB4 I see no reason why string tokens should be used. You can use InjectionToken there.
  • Royalsmed
    Royalsmed about 6 years
    Can you please explain a bit more I see no reason why string tokens should be used. You can use InjectionToken there
  • Estus Flask
    Estus Flask about 6 years
    Because string tokens provide global-like behaviour. You can use token in another module without importing it. They have some drawbacks as globals - loose coupling, namespace pollution and collisions. String tokens were considered antipatterns for these reasons and so were deprecated.
  • Royalsmed
    Royalsmed about 6 years
    ok that is clear, but what do you suggest to use? this.injector.get<string>(<any>'name'); is this the right way or an even better option to create a class and inject it instead of single properties?
  • Estus Flask
    Estus Flask about 6 years
    It depends on the case. In your case there doesn't seem to be reasons why there should be many providers. You could create a single componentConfig InjectionToken provider with name and payload props. As for ReflectiveInjector, it is usually not necessary, and I guess here too - usually you can pass data to dynamically created component through inputs.