angular2 的依賴注入包含了太多的內容,其中的一個重點就是注入器,而注入器又很是難理解,今天咱們不深刻介紹注入器的內容,能夠參考官方文檔,咱們今天來講注入器的層級。html
也就是組件獲取服務的容器會選擇具體哪個。chrome
先簡單介紹一個背景:有3個組件AppComponent 根組件、DetailList組件 ( 日誌列表組件)、Detail組件( 日誌組件)。bootstrap
這三個組件會造成一個組件樹,對應的咱們也能夠認爲每一個組件都會有一個獨立的注入器(有時候不會出現,可是能夠這麼認爲)。angular2
加入一個日誌服務LoggerService,若是按照咱們普通的入門方式,在根模塊providers 中提供LoggerService。那麼在整個應用程序中,LoggerService只有一個實例,什麼意思呢?就是說不管在哪一個組件,獲取到的都是首次建立的LoggerService,全部組件共用一個服務實例,這有時候會是一個有用的特性,好比咱們使用的全局配置。app
全局惟一不是咱們此次要驗證的重點,由於這個太普通,咱們此次要說明的是咱們如何在每一個組件中都獲取單獨的LoggerService實例,即每一個組件的實例都不一樣。這個就須要對ng2的依賴注入有所瞭解才能夠。ide
咱們逐步來講明如何實現?工具
爲了便於看到這篇短文的同窗有所瞭解,我加入一些基礎代碼。學習
1.app.module.ts 應用程序根模塊。注意此處咱們沒有在Providers中註冊loggerService。固然註冊了經過後面的方法也能夠達到咱們的目的。測試
1 import { NgModule, Optional, SkipSelf, ReflectiveInjector} from '@angular/core'; 2 import { BrowserModule } from '@angular/platform-browser'; 3 4 /* App Root */ 5 import { AppComponent } from './app.component'; 6 import { routing } from './app.routing'; 7 import { Title } from '@angular/platform-browser'; 8 import {MessagesModule, GrowlModule, ButtonModule}from 'primeng/primeng'; 9 import {AppDetailComponent}from './app-detail.component'; 10 import {AppDetailListComponent}from './app-detailList.component'; 11 import {LoggerService}from './logger.service'; 12 let allTitle:string="郭志奇"; 13 14 @NgModule({ 15 imports: [ 16 BrowserModule, 17 MessagesModule, 18 GrowlModule, ButtonModule 19 ], 20 declarations: [AppComponent, AppDetailComponent, AppDetailListComponent],//聲明當前模塊須要的指定 組件信息 21 exports: [], 22 providers: [Title], 23 bootstrap: [AppComponent] 24 }) 25 export class AppModule { 26 constructor( @Optional() @SkipSelf() parentModule: AppModule) { 27 console.log(parentModule); 28 if (parentModule) { 29 throw new Error( 30 'AppModule is already loaded. Import it in the AppModule only'); 31 } 32 } 33 }
2.app.component.ts 應用程序根組件this
1 import { Component, ViewEncapsulation, Host, ViewContainerRef, ReflectiveInjector } from '@angular/core'; 2 import { Title } from '@angular/platform-browser'; 3 import { Message } from 'primeng/primeng'; 4 import {LoggerService}from './logger.service'; 5 @Component({ 6 selector: 'my-app', 7 moduleId: module.id, 8 templateUrl: './app.component.html', 9 providers: [ 10 { provide: LoggerService, useClass: LoggerService } 11 ] 12 }) 13 export class AppComponent { 14 subtitle = '(Final)'; 15 private msgs: Message[]; 16 constructor(private title: Title, @Host() private logger: LoggerService) { 17 this.title.setTitle("AppComponent"); 18 } 19 20 show(): void { 21 this.logger.Debug(); 22 } 23 }
請注意,咱們在跟組件中providers中註冊了LoggerService。
3.app.detailList.ts 日誌列表中providers中也註冊了LoggerService
import {Component, Host}from '@angular/core'; import {LoggerService}from './logger.service'; @Component({ selector: 'my-detailList', templateUrl: './app-detailList.component.html', moduleId: module.id, providers: [ { provide: LoggerService, useClass: LoggerService } ] }) export class AppDetailListComponent { constructor( private logger: LoggerService) { } show(): void { this.logger.Debug(); } }
4.app.detail.ts 日誌組件providers沒有註冊LoggerService。
1 import {Component, Host}from '@angular/core'; 2 import {LoggerService}from './logger.service'; 3 @Component({ 4 selector: 'detail', 5 moduleId: module.id, 6 templateUrl: './app-detail.component.html', 7 providers: [ 8 // { provide: LoggerService, useClass: LoggerService } 9 ] 10 }) 11 12 export class AppDetailComponent { 13 constructor( private logger: LoggerService) { 14 15 } 16 show(): void { 17 this.logger.Debug(); 18 } 19 20 }
如今咱們經過chrome來看一下 LoggerService的層級關係。
經過查看依賴關係圖,咱們能夠看到AppComponent組件使用了單獨的LoggerService,DetailList組件也使用單獨的LoggerService 實例,而Detail組件使用的是父組件DetailList的LoggerService實例。
目前來看沒有達到咱們的要求,咱們的要求是每一個組件都有單獨的LoggerService實例,那麼咱們假設Detail組件的providers是咱們忘記輸入的,很難測試出緣由所在。那麼咱們加入一個@Host()來限制注入器的查找範圍。
對於注入器的向上查找方式,請參考官方文檔。
爲了便於調試,咱們加入@Host().
@Host
裝飾器將把往上搜索的行爲截止在 宿主組件
detail.ts 提示detail組件加入@Host()裝飾器
1 import {Component, Host}from '@angular/core'; 2 import {LoggerService}from './logger.service'; 3 @Component({ 4 selector: 'detail', 5 moduleId: module.id, 6 templateUrl: './app-detail.component.html', 7 providers: [ 8 // { provide: LoggerService, useClass: LoggerService } 9 ] 10 }) 11 12 export class AppDetailComponent { 13 constructor( @Host() private logger: LoggerService) { 14 15 } 16 show(): void { 17 this.logger.Debug(); 18 } 19 20 }
會提示找不到LoggerService的實例,@Host()的做用就是限制注入器查找到當前組件就中止,不會繼續往上查找。因此會出現找不到Providers的錯誤。
加上providers 的結果就是咱們想要的了。
完美的解決了多組件使用單獨服務實例的問題。
總結:
1.若是要使組件單獨使用服務,那麼首先要在providers 中單獨註冊該服務。很容易理解
2.爲了更好的檢測可能出現的問題,在組件服務上加入@Host()裝飾器,能夠儘可能早的拋出錯誤信息
3.使用ng2的debug工具
4.要明確各組件之間的關係,由於不一樣的組件關係會致使服務的實例的不一樣
5.服務儘可能是模塊級,不是應用級。
angular2,一個值得學習的東西。