在Angular中使用依賴註入(DI)的時候,我們一般會使用 。其實要做同樣的事我們還有另外一個選擇: 。 允許我們定義只對組件的view可見的provider。下麵我們用例子詳細的說明這一點。 假設我們有一個簡單的服務: 這個服務很簡單,只需要列印出在哪裡調用了該服務。 然後有一個子組件,是用來投 ...
在Angular中使用依賴註入(DI)的時候,我們一般會使用providers
。其實要做同樣的事我們還有另外一個選擇:viewProviders
。
viewProviders
允許我們定義只對組件的view可見的provider。下麵我們用例子詳細的說明這一點。
假設我們有一個簡單的服務:
// myService.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class MyService{
testIfGetService(where){
console.log('Got My Service in ' + where);
}
}
這個服務很簡單,只需要列印出在哪裡調用了該服務。
然後有一個子組件,是用來投射到父組件裡面的(等會將會看到):
// child.component.ts
import { Component } from '@angular/core';
import { MyService } from './myService.service';
@Component({
selector: 'vp-child',
template: `
<div>This is child!!!</div>
`
})
export class VPChild{
constructor(
private service: MyService
){
this.service.testIfGetService('child');
}
}
這個組件註入了MyService
服務,調用MyService
的testIfGetService
方法,並傳入child
表明這是在child組件調用的。
還有另外一個子組件,這個組件是用來放在父組件的模板(template)裡面的:
// viewChild.component.ts
import { Component } from '@angular/core';
import { MyService } from './myService.service';
@Component({
selector: 'vp-viewchild',
template: `
<div>This is viewChild!!!</div>
`
})
export class ViewVPChild{
constructor(
private service: MyService
){
this.service.testIfGetService('viewChild');
}
}
這裡同樣註入MyService
服務,調用MyService
服務的testIfGetService
方法,並傳入viewChild
。
最後是父組件:
// parent.component.ts
import { Component } from '@angular/core';
import { MyService } from './myService.service';
@Component({
selector: 'vp-parent',
template: `
<div>This is parent!!!</div>
<ng-content></ng-content>
<vp-viewchild></vp-viewchild>
`,
providers: [MyService]
})
export class VPParent{
constructor(
private service: MyService
){
this.service.testIfGetService('parent');
}
}
在父組件,用providers
註冊MyService
,然後調用MyService
的testIfGetService
傳入parent
。
然後就像這樣使用父組件:
<vp-parent>
<vp-child></vp-child>
</vp-parent>
運行程式,控制台列印出了結果:
一切就像預期那樣!!
然後,我們用viewProviders
代替providers
註冊MyService
,看看會發生什麼:
// parent.component.ts
import { Component } from '@angular/core';
import { MyService } from './myService.service';
@Component({
selector: 'vp-parent',
template: `
<div>This is parent!!!</div>
<ng-content></ng-content>
<vp-viewchild></vp-viewchild>
`,
viewProviders: [MyService] // <---
})
export class VPParent{
constructor(
private service: MyService
){
this.service.testIfGetService('parent');
}
}
這樣修改之後,運行程式,發現報錯了:
如果把contentChild註釋掉,就像這樣:
<vp-parent>
<!-- <vp-child></vp-child> -->
</vp-parent>
是不會報錯的:
這就說明,在父組件用viewProviders
註冊的provider,對contentChildren是不可見的。而使用providers
註冊的provider,對viewChildren和contentChildren都可見!
補充說明:組件會逐級向上尋找provider,直到找到為止,否則就會拋出錯誤。就像這裡:
<vp-parent>
<vp-child></vp-child>
</vp-parent>
vp-child
往上找MyService
的provider,結果在vp-parent
找到了。但是在用viewProviders
的時候,vp-child
往上找,也就是到vp-parent
,結果沒找到,然後又去找vp-parent
的父級,還是沒找到(因為在這個例子里,我們只在vp-parent
註冊了MyService
),然後又繼續往上找……如此找到邊界也沒找到,所以拋出了一個錯誤。如果你不希望這樣,可以使用@Host
做出限制,就像這樣:
constructor(
@Host() private service: MyService
){}
關於@Host()本文不作展開,有興趣可以自行google。