Angular 5注入器 - 如何注入字符串

6

我正在尝试将字符串注入到我的Angular组件中。 下面的代码可以正常工作,但它会给出一个弃用警告: get已被弃用:从v4.0.0开始使用Type或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');
  }
}

我尝试使用这份非常简短且不完整的文档Angular 5 Injector,来实现类似下面的功能,但是我不想使用useValueuseClass,我需要被注入的实际值。

    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

我在这里真的卡住了,文档完全没有帮助。
我需要这个用于动态组件加载和注入。完整的 Plunker 可以在此处找到:Plunker 动态组件加载和注入

你为什么说你不想使用 useValueuseClass - Daniel W Strimpel
@DanielWStrimpel因为我不是在嘲笑这些值。我需要捕获实际注入的值,而不是在我的注入失败时退回到useValue。 - Royalsmed
抱歉,我不太明白。useValueuseClass提供者值只是告诉Angular您是否提供静态值或要实例化的类。这些看起来可能是UI字符串。您是否考虑过使用翻译库来注入这些字符串?一个例子是ngx-translate。 - Daniel W Strimpel
2个回答

15

首先你需要创建一个 InjectionToken (这里在 values.ts 文件中):

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

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

那么您需要将其导入到您的模块中,并在请求该令牌时提供一个值:

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

然后您需要在想要注入值的地方导入该令牌,并使用@Inject()

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

Stackblitz 上的示例

查看 ReflectiveInjector,它被认为是慢的,建议改用Injector.create


实际上,那不是我正在寻找的,我可以通过上面的代码和useValue来实现相同的效果。{ provide: name, useValue: 'bs1'}, { provide: caption, useValue: 'bs2'}注入的值是动态的,并来自另一个服务。 - Royalsmed
我不明白,如果这些值是由一个服务提供的,那么这个服务不应该被注入吗?你是想创建一个服务,将其与运行时注入相结合,并动态地改变将在创建新实例时注入的值吗? - Jason Goemaat
@Royalsmed 那么这个问题其实是 XY 问题。通常情况下,对于常规任务,您根本不需要使用 ReflectiveInjector。如果您需要它,那么可以确定您正在做一些错误的事情。 - Estus Flask
@estus,我觉得我没有做错什么,但是我可能错了:p 这是一个完整的 Plunker,展示了我想要实现的内容[链接](https://embed.plnkr.co/p7VHB4/)。 - Royalsmed

8

get方法已经被弃用:从v4.0.0开始,请使用Type或InjectionToken。

警告意味着injector.get是一个泛型方法,它需要类型参数。这正是Injector文档所述的内容:

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

考虑到name字符串标记应该解析为字符串,应该这样写:

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

字符串令牌已被弃用,现在期望一个令牌要么是一个函数,要么是 InjectionToken 实例。

然而,我不想使用 useValue 或 useClass,我需要的是注入的实际值。

所有依赖项应该被注册为模块或组件提供者,以便进行注入。


不幸的是,它并不是这样工作的。this.injector.get<string>('name'); 不起作用,因为 Argument of type '"name"' is not assignable to parameter of type 'InjectionToken<string> | Type<string>'。 - Royalsmed
我已经更新了答案并附上了解释。字符串令牌已被弃用,您必须使用 <any> 进行欺骗。在您的情况下 https://embed.plnkr.co/p7VHB4/ 我看不出为什么应该使用字符串令牌。您可以在那里使用 InjectionToken。 - Estus Flask
你能否再解释一下“我看不出为什么要使用字符串标记。你可以在那里使用InjectionToken”这句话的意思? - Royalsmed
因为字符串标记提供类似全局的行为。您可以在另一个模块中使用标记而无需导入它。它们具有一些全局变量的缺点 - 松散耦合,命名空间污染和冲突。由于这些原因,字符串标记被认为是反模式,因此已被弃用。 - Estus Flask
好的,这很清楚,但你建议使用什么?this.injector.get<string>(<any>'name'); 这是正确的方式还是创建一个类并注入它而不是单个属性更好? - Royalsmed
这取决于具体情况。在您的情况下,似乎没有理由使用多个提供程序。您可以创建一个带有“名称”和“有效载荷”属性的单个componentConfig InjectionToken提供程序。至于ReflectiveInjector,通常不是必需的,我猜在这里也是如此 - 通常可以通过“inputs”将数据传递给动态创建的组件。 - Estus Flask

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接