【问题标题】:Angular 2 - service - dependency injection from another serviceAngular 2 - 服务 - 来自另一个服务的依赖注入
【发布时间】:2016-08-07 16:44:37
【问题描述】:

我在 Angular 2 中编写了两项服务。其中一项是 Http 的基本自定义类,其中包含一些自定义功能(目前看起来很基础,但会不断扩展):

ServerComms.ts

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';

@Injectable()
export class ServerComms {
    private url = 'myservice.com/service/';

    constructor (public http: Http) {
        // do nothing
    }

    get(options) {
        var req = http.get('https://' + options.name + url);

        if (options.timout) {
            req.timeout(options.timeout);
        }

        return req;
    }
}

另一个类TicketService利用上面的这个类,并调用服务中的一个方法。这定义如下:

TicketService.ts

import {Injectable} from 'angular2/core';
import {ServerComms} from './ServerComms';

@Injectable()
export class TicketService {
    constructor (private serverComms: ServerComms) {
        // do nothing
    }

    getTickets() {
        serverComms.get({
            name: 'mycompany',
            timeout: 15000
        })
            .subscribe(data => console.log(data));
    }
}

但是,每当我尝试此操作时,都会收到以下错误:

"No provider for ServerComms! (App -> TicketService -> ServerComms)"

我不明白为什么?当然,我不需要注入其他服务所依赖的每项服务吗?这会变得相当乏味吗?这在 Angular 1.x 中是可以实现的——我如何在 Angular 2 中实现同样的目标?

这是正确的做法吗?

【问题讨论】:

    标签: javascript dependency-injection dependencies angular


    【解决方案1】:

    简而言之,由于注入器是在组件级别定义的,因此启动调用服务的组件必须看到相应的提供程序。第一个(直接调用),还有另一个间接调用(由之前的服务调用)。

    我们来取样。我有以下应用程序:

    • 组件AppComponent:我的应用程序的主要组件,在bootstrap函数中创建Angular2应用程序时提供

      @Component({
        selector: 'my-app', 
          template: `
            <child></child>
          `,
          (...)
          directives: [ ChildComponent ]
      })
      export class AppComponent {
      }
      
    • 组件ChildComponent:将在AppComponent组件中使用的子组件

      @Component({
          selector: 'child', 
          template: `
            {{data | json}}<br/>
            <a href="#" (click)="getData()">Get data</a>
          `,
          (...)
      })
      export class ChildComponent {
        constructor(service1:Service1) {
          this.service1 = service1;
        }
      
        getData() {
          this.data = this.service1.getData();
            return false; 
        }
      }
      
    • Service1Service2 两个服务:Service1ChildComponent 使用,Service2Service1 使用

      @Injectable()
      export class Service1 {
        constructor(service2:Service2) {
          this.service2 = service2;
        }
      
        getData() {
          return this.service2.getData();
        }
      }
      

      @Injectable()
      export class Service2 {
      
        getData() {
          return [
            { message: 'message1' },
            { message: 'message2' }
          ];
        }
      }
      

    以下是所有这些元素及其关系的概述:

    Application
         |
    AppComponent
         |
    ChildComponent
      getData()     --- Service1 --- Service2
    

    在这样的应用程序中,我们有三个注入器:

    • 可以使用bootstrap函数的第二个参数配置的应用程序注入器
    • 可以使用该组件的providers 属性配置的AppComponent 注入器。它可以“看到”应用程序注入器中定义的元素。这意味着如果在此提供程序中找不到提供程序,它将自动在此父注入器中查找。如果后者找不到,则会抛出“provider not found”错误。
    • ChildComponent 注入器将遵循与 AppComponent 相同的规则。要注入组件执行的注入链中涉及的元素,将首先在此注入器中查找提供程序,然后在 AppComponent 中查找,最后在应用程序中查找。

    这意味着当尝试将Service1 注入ChildComponent 构造函数时,Angular2 将查看ChildComponent 注入器,然后查看AppComponent 注入器,最后进入应用程序之一。

    由于Service2需要注入Service1,所以会进行同样的解析处理:ChildComponent注入器,AppComponent一和应用一。

    这意味着Service1Service2 可以根据您的需要使用组件的providers 属性和应用程序注入器的bootstrap 函数的第二个参数在每个级别指定。

    这允许共享一组元素的依赖实例:

    • 如果您在应用程序级别定义提供程序,则相应创建的实例将由整个应用程序(所有组件、所有服务……)共享。
    • 如果您在组件级别定义提供程序,则该实例将由组件本身、其子组件以及依赖链中涉及的所有“服务”共享。

    所以它非常强大,您可以根据自己的需要随意组织。

    这里是对应的 plunkr,所以你可以玩一下:https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview

    Angular2 文档中的此链接可以帮助您:https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html

    【讨论】:

      【解决方案2】:

      你当然知道。

      在 Angular2 中,有多个注入器。喷射器使用providers 组件数组进行配置。当组件具有providers 数组时,会在树中的该点创建一个注入器。当组件、指令和服务需要解决它们的依赖关系时,它们会查找注入器树以找到它们。因此,我们需要使用提供者配置该树。

      从概念上讲,我喜欢认为有一个注入器树覆盖了组件树,但它比组件树更稀疏。

      同样,从概念上讲,我们必须配置这个注入器树,以便在树中的适当位置“提供”依赖项。 Angular 2 没有创建单独的树,而是重用组件树来执行此操作。所以即使感觉就像我们在组件树上配置依赖,我喜欢认为我在注入器树上配置依赖(它恰好覆盖了组件树,所以我必须使用组件来配置它)。

      清如泥?

      Angular 2 有一个注入器树的原因是允许非单例服务——即,能够在注入器树的不同点创建特定服务的多个实例。如果您想要类似 Angular 1 的功能(仅单例服务),请在根组件中提供所有服务。


      构建您的应用程序,然后返回并配置注入器树(使用组件)。我就是这么想的。如果您在另一个项目中重用组件,则很可能需要更改 providers 数组,因为新项目可能需要不同的注入器树。

      【讨论】:

        【解决方案3】:

        好吧,我猜你应该在全球范围内提供这两种服务:

        bootstrap(App, [service1, service2]);
        

        或提供给使用它们的组件:

        @Component({providers: [service1, service2]})
        

        @Injectable 装饰器添加必要的元数据来跟踪依赖项,但不提供它们。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-21
          • 1970-01-01
          • 2019-03-04
          • 2017-05-20
          • 2022-12-20
          • 2020-01-27
          • 2020-09-09
          • 2015-07-21
          相关资源
          最近更新 更多