在角度2(Beta)中将一项服务注入另一项服务的最佳方法是什么?

时间:2016-01-15 04:14:36

标签: javascript angular

我知道如何将服务注入组件(通过@Component),但是如何使用DI来传递组件之外的服务呢?

换句话说,我不想这样做:

export class MyFirstSvc {

}

export class MySecondSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}

export class MyThirdSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}

7 个答案:

答案 0 :(得分:46)

是的,首先要在每个要注入的服务上添加@Injectable装饰器。事实上,Injectable名称有点阴险。这并不意味着课程将是可注射的"但它会装饰,因此可以注入构造函数参数。有关详细信息,请参阅此github问题:https://github.com/angular/angular/issues/4404

以下是我对注射机制的理解。为类设置@Injectable装饰器时,Angular将尝试为当前执行链的注入器中的相应类型创建或获取实例。实际上,Angular2应用程序不仅有一个注入器,而且还有一个注入器树。它们与整个应用程序和组件隐式关联。此级别的一个关键特性是它们以分层方式链接在一起。这个注入器树映射组件树。没有为"服务"。

定义注射器

我们来试试吧。我有以下申请:

  • 组件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注入器。它可以&#34;看&#34;应用程序注入器中定义的元素。这意味着如果在此提供程序中找不到提供程序,它将自动查找此父注入程序。如果在后者中找不到,则找不到&#34;提供商&#34;错误将被抛出。
  • ChildComponent注入器遵循与AppComponent规则相同的规则。要注入为组件执行的注入链中涉及的元素,将首先在此注入器中查找提供者,然后在AppComponent中查找提供者,最后在应用程序中查找提供者。

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

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

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

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

  • 如果您在应用程序级别定义提供程序,则整个应用程序将共享对应的已创建实例(所有组件,所有服务,...)。
  • 如果您在组件级别定义提供程序,则该实例将由组件本身,其子组件和所有&#34;服务&#34;参与依赖链。

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

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

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

希望它可以帮助你(对不起,答案很长), 亨利

答案 1 :(得分:5)

  • 在您打算使用它们的地方或之上“提供”您的服务,例如,如果每个服务只有一个实例(单例),您可以使用bootstrap()将它们放在应用程序的根目录下。
  • 在依赖他人的任何服务上使用@Injectable()装饰器。
  • 将其他服务注入到从属服务的构造函数中。

boot.ts

import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {MyFirstSvc} from '../services/MyFirstSvc';
import {MySecondSvc} from '../services/MySecondSvc';

bootstrap(AppComponent, [MyFirstSvc, MySecondSvc]);

MySecondSvc.ts

import {Injectable} from 'angular2/core';
import {MyFirstSvc} from '../services/MyFirstSvc';

@Injectable()
export class MySecondSvc {
  constructor(private _firstSvc:MyFirstSvc) {}
  getValue() {
    return this._firstSvc.value;
  }
}

有关其他文件,请参阅 Plunker

服务DI有点奇怪的是它依赖于组件。例如,MySecondSvc是在组件请求时创建的,并且取决于组件树中“提供”MyFirstSvc的位置,这可能会影响MyFirstSvc实例注入MySecondSvc实例}。这里将对此进行更多讨论:Can you only inject services into services through bootstrap?

答案 2 :(得分:4)

服务被视为在组件之间共享。所以,假设我有一项服务,我可以在不同的组件中使用它。

这里在这个答案中,我向您展示了一个服务,它接受来自一个组件的数据并将该数据发送到其他组件。

我使用了路由,共享服务,共享对象的概念。 我希望这有助于您了解共享服务的基本知识。

注意: @Injectable decorater用于使服务可注入。

Answer

<强> Boot.ts

import {Component,bind} from 'angular2/core';

import {bootstrap} from 'angular2/platform/browser';

import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';

import {SharedService} from 'src/sharedService';

import {ComponentFirst} from 'src/cone';
import {ComponentTwo} from 'src/ctwo';


@Component({
  selector: 'my-app',
  directives: [ROUTER_DIRECTIVES],
  template: `
    <h1>
      Home
    </h1> 

    <router-outlet></router-outlet>
      `,

})

@RouteConfig([
  {path:'/component-first', name: 'ComponentFirst', component: ComponentFirst}
  {path:'/component-two', name: 'ComponentTwo', component: ComponentTwo}

])

export class AppComponent implements OnInit {

  constructor(router:Router)
  {
    this.router=router;
  }

    ngOnInit() {
    console.log('ngOnInit'); 
    this.router.navigate(['/ComponentFirst']);
  }



}

    bootstrap(AppComponent, [SharedService,
    ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
    ]);

<强> FirstComponent

import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
  //selector: 'f',
  template: `
    <div><input #myVal type="text" >
    <button (click)="send(myVal.value)">Send</button>
      `,

})

export class ComponentFirst   {

  constructor(service:SharedService,router:Router){
    this.service=service;
    this.router=router;
  }

  send(str){
    console.log(str);
    this.service.saveData(str); 
    console.log('str');
    this.router.navigate(['/ComponentTwo']);
  }

}

<强> SecondComponent

import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
  //selector: 'f',
  template: `
    <h1>{{myName}}</h1>
    <button (click)="back()">Back<button>
      `,

})

export class ComponentTwo   {

  constructor(router:Router,service:SharedService)
  {
    this.router=router;
    this.service=service;
    console.log('cone called');
    this.myName=service.getData();
  }
  back()
  {
     console.log('Back called');
    this.router.navigate(['/ComponentFirst']);
  }

}

SharedService和共享对象

import {Component, Injectable,Input,Output,EventEmitter} from 'angular2/core'

// Name Service
export interface myData {
   name:string;
}



@Injectable()
export class SharedService {
  sharingData: myData={name:"nyks"};
  saveData(str){
    console.log('save data function called' + str + this.sharingData.name);
    this.sharingData.name=str; 
  }
  getData:string()
  {
    console.log('get data function called');
    return this.sharingData.name;
  }
} 

答案 3 :(得分:2)

以某种方式@Injectable在Angular 2.0.0-beta.17 中连接时,对ComponentA进行连接 - &gt; ServiceB - &gt; ServiceC。

我采用了这种方法:

  1. 引用@ ComponentA的提供者字段中的所有服务。
  2. 在ServiceB中,使用构造函数中的@Inject注释来连接ServiceC。
  3. 运行this Plunker以查看下面的示例或视图代码

    app.ts

    @Component({selector: 'my-app',
        template: `Hello! This is my app <br/><br/><overview></overview>`,
        directives: [OverviewComponent]
    })
    class AppComponent {}
    
    bootstrap(AppComponent);
    

    overview.ts

    import {Component, bind} from 'angular2/core';
    import {OverviewService} from "../services/overview-service";
    import {PropertiesService} from "../services/properties-service";
    
    @Component({
        selector: 'overview',
        template: `Overview listing here!`,
        providers:[OverviewService, PropertiesService] // Include BOTH services!
    })
    
    export default class OverviewComponent {
    
        private propertiesService : OverviewService;
    
        constructor( overviewService: OverviewService) {
            this.propertiesService = overviewService;
            overviewService.logHello();
        }
    }
    

    概述-service.ts

    import {PropertiesService} from "./properties-service";
    import {Inject} from 'angular2/core';
    
    export class OverviewService {
    
        private propertiesService:PropertiesService;
    
        // Using @Inject in constructor
        constructor(@Inject(PropertiesService) propertiesService:PropertiesService){
            this.propertiesService = propertiesService;
        }
    
        logHello(){
            console.log("hello");
            this.propertiesService.logHi();
        }
    }
    

    属性 - service.ts

    // Using @Injectable here doesn't make a difference
    export class PropertiesService {
    
        logHi(){
            console.log("hi");
        }
    }
    

答案 4 :(得分:2)

不确定是否仍然需要答案,所以我会继续尝试回答这个问题。

考虑以下示例,其中我们有一个Component,它使用服务在其模板中填充某些值,如下所示

testComponent.component.ts

import { Component } from "@angular/core"
import { DataService } from "./data.service"
@Component({
    selector:"test-component",
    template:`<ul>
             <li *ngFor="let person of persons">{{ person.name }}</li>
             </ul>
})

export class TestComponent {
  persons:<Array>;
  constructor(private _dataService:DataService){
    this.persons = this._dataService.getPersons()
  }
}

上面的代码非常简单,它将尝试获取从DataService返回的任何getPersons。 DataService文件位于下方。

data.service.ts

export class DataService {

persons:<Array>;

constructor(){
    this.persons = [
      {name: "Apoorv"},
      {name: "Bryce"},
      {name: "Steve"}
    ]
}

getPersons(){

return this.persons

}

如果不使用@Injectable装饰器,上面的代码将完美无缺。但是当我们的服务(在这种情况下为DataService)需要一些依赖性时,问题将开始。断点续传。如果我们更改我们的data.service.ts文件,我们会收到错误Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.

import { Http } from '@angular/http';
export class DataService {

persons:<Array>;

constructor(){
    this.persons = [
      {name: "Apoorv"},
      {name: "Bryce"},
      {name: "Steve"}
    ]
}

getPersons(){

return this.persons

}

这与装饰器在Angular 2中的运行方式有关。请阅读https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html以深入了解此问题。

上面的代码也不起作用,因为我们也必须在我们的bootstrap模块中导入HTTP。

但我可以建议的一个拇指规则是,如果你的服务文件需要依赖,那么你应该用装饰器@Injectable来装饰那个类。

参考:https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html

答案 5 :(得分:0)

要做的第一件事是使用@Injectable注释注释所有服务。注意注释末尾的括号,如果没有这个,这个解决方案就无法工作。

完成后,我们可以使用构造函数注入将服务注入到彼此中:

@Injectable()
export class MyFirstSvc {

}

@Injectable()
export class MySecondSvc {
    constructor(helpfulService: MyFirstSvc) {        
    }
}

@Injectable()
export class MyThirdSvc {
    constructor(helpfulService: MyFirstSvc) {        
    }
}

答案 6 :(得分:0)

首先您需要提供服务

你可以在bootstrap方法中提供它:

bootstrap(AppComponent,[MyFirstSvc]);

或应用程序组件或任何其他组件,具体取决于您的需求。:

@Component({
    ...
      providers:[MyFirstSvc]
}
...

然后使用构造函数注入您的服务:

export class MySecondSvc {
      constructor(private myFirstSvc : MyFirstSvc ){}
}
相关问题