24

我一直试图弄清楚(DI)依赖注入在Angular2中是如何工作的。每次尝试将服务/或类注入我的组件时,我都会遇到很多问题/问题。

从不同的谷歌文章中,我需要providers: []在组件配置中使用,或者有时我需要@Inject()在我的构造函数中使用或直接注入bootstrap(app, [service])?我也看过一些文章想让我把@injectabledecorator。

例如:要注入 Http,我只需要将import{Http}Http 放入 providers 中,但是对于 FormBuilder,我需要@Inject()在构造函数中使用。

什么时候使用什么有经验法则吗?您能否提供一些示例代码片段?谢谢 :-)

4

5 回答 5

17

广泛的问题,TL;DR版本


@Injectable()

  • 是一个装饰器,它告诉被typescript装饰的类有dependencies,并不意味着这个类可以注入其他类。

  • 然后 TypeScript 了解到它需要在构造时通过imported依赖关系将所需的元数据注入到装饰类中。

引导程序(应用程序,[服务])

  • bootstrap() 负责在启动时为我们的应用程序创建一个根注入器。它将提供者列表作为第二个参数,在创建时将直接传递给注入器。

  • 您使用将在许多地方使用的服务引导您的应用程序,例如Http,这也意味着您不需要编写providers: [Http]类配置。

提供者:[服务]

  • providers 还负责将所有服务的参数传递给Injector.

  • 如果没有提供服务,您可以将服务放在提供者bootstrap()中。并且仅在少数地方需要。

@注入()

  • 也是一个装饰器,一个函数可以
    像这样实际注入这些服务。constructor(@Inject(NameService) nameService)
  • 但是如果你使用 TS,你需要做的就是这个constructor(nameService: NameService),打字稿会处理剩下的。

延伸阅读

希望这可以帮助。:)

于 2016-03-20T07:30:53.960 回答
13

Angular2 中的依赖注入依赖于链接到组件树的分层注入器。

这意味着您可以在不同级别配置提供程序:

  • 对于引导它时的整个应用程序。在这种情况下,所有子注入器(组件注入器)都将看到此提供程序并共享与之关联的实例。交互时会是同一个实例
  • 对于特定组件及其子组件。与以前相同,但针对特定组件。其他组件将看不到此提供程序。如果您重新定义上面定义的内容(例如在引导时),将使用此提供程序。所以你可以覆盖一些东西。
  • 对于服务。没有与之关联的提供者。他们使用来自触发元素的注入器(直接=组件或间接=触发服务链调用的组件)

关于你的其他问题:

  • @可注射。要注入一个类,你需要一个装饰器。组件有一个(@Component 之一),但服务是简单的类。如果服务需要在其中注入依赖项,则需要此装饰器。
  • @注入。在大多数情况下,构造函数参数的类型足以让 Angular2 决定注入什么。在某些情况下(例如,如果您明确使用 OpaqueToken 而不是类来注册提供程序),您需要指定一些有关注入内容的提示。在这种情况下,您需要使用@Inject。

有关其他详细信息,请参阅这些问题:

于 2016-03-20T07:38:37.553 回答
4

我需要使用提供者:[]

为了使依赖注入能够为您创建实例,您需要在某处为这些类(或其他值)注册提供者。

您注册提供者的位置决定了创建值的范围。Angulars DI 是分层的。
如果您在树的根部注册提供程序


>=RC.5

@NgModule({
  providers: [/*providers*/]
  ...
})

或者对于延迟加载的模块

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}

<=RC.4

(bootstrap(AppComponent, [Providers})@Component(selector: 'app-component', providers: [Providers])(根组件)


那么所有请求实例的组件和服务都会获得相同的实例。

如果提供者在其中一个子组件中注册,则为该组件的后代提供一个新的(不同的)实例。

如果组件请求一个实例(通过构造函数参数),DI 会“向上”查找组件树(从叶子开始到根)并获取它找到的第一个提供程序。如果之前已经创建了此提供程序的实例,则使用此实例,否则将创建一个新实例。

@注入()

当组件或服务向 DI 请求一个值时,例如

constructor(someField:SomeType) {}

DI 按类型查找提供程序SomeType。如果@Inject(SomeType)添加

constructor(@Inject(SomeType) someField:SomeType) {}

DI 通过传递给的参数查找提供程序@Inject()。在上面的示例中,传递给的参数与@Inject()参数的类型相同,因此@Inject(SomeType)是多余的。

但是,在某些情况下,您希望自定义行为,例如注入配置设置。

constructor(@Inject('someName') someField:string) {}

当您注册了多个时,该类型string不足以区分特定的配置设置。
配置值需要在某处注册为提供者


>=RC.5

@NgModule({
  providers: [{provide: 'someName', useValue: 'abcdefg'})]
  ...
})
export class AppModule {}

<=RC.4

bootstrap(AppComponent, [provide('someName', {useValue: 'abcdefg'})])

@Inject()因此,FormBuilder如果构造函数看起来像,则不需要

constructor(formBuilder: FormBuilder) {}
于 2016-03-20T07:34:35.407 回答
2

我将添加一些我在其他答案中没有提到的东西。(在我写这篇文章的时候,这意味着来自 Thierry、Günter 和 A_Singh 的答案)。

  • 始终添加Injectable()到您创建的服务中。尽管仅当您的服务本身需要注入某些内容时才需要它,但始终包含它是最佳实践。
  • providers指令/组件上的数组providers和 NgModules 中的数组是注册非内置提供程序的仅有的两种方法。(我们不必注册的内置对象的示例是ElementRef,ApplicationRef等。我们可以简单地注入这些。)
  • 当一个组件有一个providers数组时,该组件就会获得一个 Angular 注入器。当某些东西想要注入依赖项(在构造函数中指定)时,会咨询注入器。我喜欢将注入器树视为比组件树更备用的树。第一个可以满足依赖请求的注入器会这样做。这种注入器的层次结构允许依赖项是单例的,也可以不是单例的。
于 2016-03-21T19:54:58.443 回答
0

为什么是@Injectable()?

@Injectable() 将一个类标记为可用于注入器进行实例化。一般来说,注入器在尝试实例化未标记为@Injectable() 的类时会报错。

碰巧的是,我们可以在 HeroService 的第一个版本中省略 @Injectable(),因为它没有注入参数。但是我们现在必须拥有它,因为我们的服务具有注入的依赖项。我们需要它,因为 Angular 需要构造函数参数元数据才能注入 Logger。

建议:将@INJECTABLE() 添加到每个服务类我们建议将@Injectable() 添加到每个服务类,即使是那些没有依赖关系的服务类,因此在技术上不需要它。原因如下:

未来证明:以后添加依赖项时无需记住@Injectable()。

一致性:所有服务都遵循相同的规则,我们不必怀疑为什么缺少装饰器。

注入器还负责实例化 HeroesComponent 等组件。为什么我们没有将 HeroesComponent 标记为 @Injectable()?

如果我们真的想添加它,我们可以添加它。这不是必需的,因为 HeroesComponent 已经用 @Component 进行了标记,并且这个装饰器类(如 @Directive 和 @Pipe,我们稍后会了解)是 InjectableMetadata 的子类型。事实上,InjectableMetadata 装饰器将一个类标识为注入器实例化的目标。

来源:https ://angular.io/docs/ts/latest/guide/dependency-injection.html

于 2016-08-21T04:14:58.067 回答