【问题标题】:How to use Dependency Injection (DI) correctly in Angular2?如何在Angular2中正确使用依赖注入(DI)?
【发布时间】:2016-07-06 16:38:58
【问题描述】:

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

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

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

对于何时使用什么有什么经验法则吗?你能提供一些示例代码sn-p吗?谢谢你:-)

【问题讨论】:

标签: angular angular2-services angular2-di


【解决方案1】:

广泛的问题,TL;DR 版本


@Injectable()

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

  • 然后 TypeScript 明白它需要在构造时通过使用 imported 依赖项将所需的元数据注入到装饰类中。

bootstrap(app, [service])

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

  • 您可以使用将在许多地方使用的服务(如 Http)引导您的应用程序,这也意味着您无需在类配置中编写 providers: [Http]

    李>

providers: [service]

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

  • 如果不是bootstrap()ped,则将服务放入提供者中。并且仅在少数地方需要。

@Inject()

  • 也是一个装饰器一个函数,它执行实际注入这些服务的工作
    像这样。 constructor(@Inject(NameService) nameService)
  • 但是,如果您使用 TS,您需要做的就是这个 constructor(nameService: NameService),然后 typescript 会处理其余的事情。

进一步阅读

希望这会有所帮助。 :)

【讨论】:

  • 一些链接不起作用,特别是指向 Angular 的顶部链接。可以更新一下吗?
  • 嗨 Ankit,我对@injectable 装饰器有点困惑,你能否描述一下“并不意味着这个类可以注入其他类”。更详细一点。
  • @Injectable() 仅当该服务具有依赖关系时才需要该服务(即:构造函数参数中有某些内容)。换句话说,@Injectable() 不提供服务,服务。它只是注入依赖项。
【解决方案2】:

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

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

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

关于您的其他问题:

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

有关更多详细信息,请参阅这些问题:

【讨论】:

    【解决方案3】:

    我需要使用提供者:[]

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

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


    >=RC.5

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

    或者对于延迟加载的模块

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

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


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

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

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

    @Inject()

    当组件或服务向 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 {}
    

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

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

    constructor(formBuilder: FormBuilder) {}
    

    【讨论】:

    • 我最近发现了一个案例,@Inject(type) myVar:typemyVar:type 似乎不等价。在注入我的一项服务时,前者似乎绕过了方法检查,因此我可以编写myService.serviceFunc() 并且如果服务上不存在serviceFunc(),TypeScript 不会抱怨。但是,如果我使用第二种方法,它确实会给我一个错误(应该如此)并且不会编译。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      为什么要@Injectable()?

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

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

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

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

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

      Injector 还负责实例化像 HeroesComponent 这样的组件。为什么我们没有将 HeroesComponent 标记为 @Injectable()?

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-27
        • 1970-01-01
        • 1970-01-01
        • 2020-05-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多