簡體   English   中英

在角度2(Beta)中將一項服務注入另一項服務的最佳方法是什么?

[英]What's the best way to inject one service into another in angular 2 (Beta)?

我知道如何將服務注入組件(通過@Component),但是如何使用DI來傳遞組件之外的服務?

換句話說,我不想這樣做:

export class MyFirstSvc {

}

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

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

是的,首先要在每個要注入的服務上添加@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 { } 
  • Component ChildComponent :將在AppComponent組件中使用的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; } } 
  • 兩個服務, Service1Service2Service1時使用的ChildComponentService2通過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函數的第二個參數配置的應用程序注入器
  • 可以使用此組件的providers屬性配置的AppComponent注入器。 它可以“看到”應用程序注入器中定義的元素。 這意味着如果在此提供程序中找不到提供程序,它將自動查找此父注入程序。 如果在后者中找不到,則會拋出“找不到提供者”錯誤。
  • ChildComponent注入器將遵循與AppComponent相同的規則。 要注入為組件執行的注入鏈中涉及的元素,將首先在此注入器中查找提供者,然后在AppComponent者,最后在應用程序中AppComponent

這意味着,試圖將注入時, Service1ChildComponent構造,Angular2將調查ChildComponent注射器,然后進入AppComponent一個最后進入應用程序之一。

由於Service2需要被注入到Service1 ,相同分辨率的處理會做到: ChildComponent注射器, AppComponent一個應用程序之一。

這意味着可以使用組件的providers屬性和應用程序注入器的bootstrap函數的第二個參數,根據您的需要在每個級別指定Service1Service2

這允許共享一組元素的依賴項實例:

  • 如果在應用程序級別定義提供程序,則整個應用程序將共享對應的已創建實例(所有組件,所有服務,...)。
  • 如果在組件級別定義提供程序,則實例將由組件本身,其子組件以及依賴關系鏈中涉及的所有“服務”共享。

因此它非常強大,您可以根據自己的需要和需求自由組織。

這是相應的plunkr所以你可以玩它: https ://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p = preview。

Angular2文檔中的這個鏈接可以幫助您: https ://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html。

希望它可以幫助你(對不起,答案很長),蒂埃里

  • 在您打算使用它們的地方或之上“提供”您的服務,例如,如果每個服務只有一個實例(單例),您可以使用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 這里將對此進行更多討論: 您是否只能通過引導程序將服務注入服務?

服務被認為是在組件之間共享。 所以,假設我有一項服務,我可以在不同的組件中使用它。

在這個答案中,我向您展示了一個服務,它接受來自一個組件的數據並將該數據發送到其他組件。

我使用了Routing,Shared-Service,Shared-Object的概念。 我希望這能幫助您了解共享服務的基本知識。

注意: @Injectable decorater用於使服務可注射。

回答

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;
  }
} 

不知何故@Injectable在Angular 2.0.0-beta.17中對我來說在連接ComponentA - > ServiceB - > ServiceC時不起作用

我采用了這種方法:

  1. 引用@ ComponentA的providers字段中的所有服務。
  2. 在ServiceB中,使用構造函數中的@Inject批注來連接ServiceC。

運行此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");
    }
}

不確定是否還需要答案,所以我會繼續嘗試回答這個問題。

請考慮以下示例,其中我們有一個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. 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

首先要做的是使用@Injectable注釋注釋所有服務。 注意注釋末尾的括號,如果沒有這個,這個解決方案將無效。

完成后,我們可以使用構造函數注入將服務注入到彼此中:

@Injectable()
export class MyFirstSvc {

}

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

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

首先您需要提供服務

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

bootstrap(AppComponent,[MyFirstSvc]);

或根據您的需要在應用程序組件或任何其他組件中:

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

然后使用構造函數注入您的服務:

export class MySecondSvc {
      constructor(private myFirstSvc : MyFirstSvc ){}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM