Angular 2 - 服务 - 来自其他服务的依赖注入

时间:2016-04-15 15:55:07

标签: javascript dependency-injection dependencies angular

我已经在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中实现相同的目标?

这是正确的方法吗?

3 个答案:

答案 0 :(得分:4)

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

我们来看一个样本。我有以下申请:

  • 组件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 Service2两个服务Service1

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

这意味着当尝试将Service1注入ChildComponent构造函数时,Angular2会查看ChildComponent注入器,然后进入AppComponent注入器,最后进入申请一。

由于Service2需要注入Service1,因此将执行相同的解决方案处理:ChildComponent注入器,AppComponent一个和应用程序一。

这意味着可以根据您的需要使用组件的Service1属性和Service2函数的第二个参数在每个级别指定providersbootstrap用于应用注射器。

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

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

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

以下是相应的plunkr,您可以使用它:https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview

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

答案 1 :(得分:2)

你当然可以。

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

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

同样,从概念上讲,我们必须配置此注入树,以便在树中的适当位置“提供”依赖关系。 Angular 2不是创建单独的树,而是重用组件树来执行此操作。因此,即使感觉我们在组件树上配置依赖关系,我还是认为我正在配置注入器树上的依赖关系(这恰好覆盖了组件树,因此我必须使用组件来配置它)。 / p>

清除泥土?

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

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

答案 2 :(得分:1)

嗯,我猜你应该全球提供两种服务:

bootstrap(App, [service1, service2]);

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

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

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