[英]Angular - Service injecting dynamic component?
我有一個工作代碼,該代碼通過服務將任何組件注入HTML:
ModalWindow.ts:
@Component({
selector: 'modal-window'
template: `
<div class="modal-dialog" role="document">
<div class="modal-content"><ng-content></ng-content></div>
</div>
`
})
export class ModalWindow {
}
Modalcontent.ts:
@Component({
selector: 'modal-content'
template: `
I'm beeing opened as modal!
`
})
export class ModalContent {
}
ModalService.ts:
/*1*/ @Injectable()
/*2*/ export class ModalService {
/*3*/
/*4*/ constructor(private _appRef: ApplicationRef, private _cfr: ComponentFactoryResolver, private _injector: Injector) {
/*5*/ }
/*6*/
/*7*/ open(content: any) {
/*8*/ const contentCmpFactory = this._cfr.resolveComponentFactory(content);
/*9*/ const windowCmpFactory = this._cfr.resolveComponentFactory(ModalWindow);
/*10*/
/*11*/ const contentCmpt = contentCmpFactory.create(this._injector);
/*12*/ const windowCmpt = windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
/*13*/
/*14*/ document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
/*15*/
/*16*/ this._appRef.attachView(contentCmpt.hostView);
/*17*/ this._appRef.attachView(windowCmpt.hostView);
/*18*/ }
/*19*/ }
應用程式:
@Component({
selector: 'my-app',
template: `
<button (click)="open()">Open modal window</button>
`,
})
結果(單擊一個調用此服務方法的按鈕時):
我已經知道contentCmpFactory
和windowCmpFactory
是什么( 第#8,9行 )
但是我不知道以后會發生什么。 關於第11行,第12行-文檔說“創建一個新組件”。
問題:
1行#12: [[contentCmpt.location.nativeElement]]
做什么? ( 文檔說它的類型是projectableNodes?: any[][]
-它們是什么意思?)
2行14號: [[windowCmpt.location.nativeElement]]
做什么?
3行#16,#17:如果我已經執行appendChild
,為什么又需要它們? ( docs說:附加視圖,以便對其進行臟檢查。-這樣嗎?)。
答案:
1) Angular使用ComponentFactory
並使用給定的元素注入器和可投影節點數組創建組件實例
windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
1.1當角度將解決依賴關系時將使用元素注入器
const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
這也是沒有延遲加載的應用程序依賴項解析算法的簡單說明。 使用延遲加載,看起來會更加復雜。
有關更多詳細信息,請參見設計文檔元素注入器與模塊注入器
1.2可投影節點是節點元素,它們在組件模板的ng-content
中被“投影”(包含)。
為了投射某些東西,我們的組件模板必須包含ng-content
節點。
@Component({
selector: 'modal-window',
template: `
<div class="modal-dialog">
<div class="modal-content">
<ng-content></ng-content> // <== place for projection
</div>
</div>
`
})
export class ModalWindow {
我們可以在父組件模板中使用上述組件,如下所示:
<modal-window>
<modal-content></modal-content>
<div>Some other content</div>
</modal-window>
因此,我們的最終結果將如下所示:
<modal-window>
<div class="modal-dialog">
<div class="modal-content">
<modal-content></modal-content> // our projectable nodes
<div>Some other content</div> // replaced ng-content
</div>
</div>
</modal-window>
因此,當我們傳遞可投影節點以創建方法時
windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
我們做與上述相同的事情。
我們正在獲取創建的早期contentCmpt
組件的host元素的引用( contentCmpt.location
)。 這是modal-content
元素。 然后,Angular將盡一切魔力將其投射到ng-content
位置。
在上面的示例中,我添加了一個div
<modal-window>
<modal-content></modal-content>
<div>Some other content</div> <== here
</modal-window>
因此,實際代碼應如下所示:
let div = document.createElement('div');
div.textContent = 'Some other content';
windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement, div]]);
結論為什么projectableNodes是any [] []?
2)在下一行
document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
我們正在引用在內存中創建的modal-window
元素。 ComponentRef
允許我們執行此操作,因為它在location
getter中存儲了對宿主元素的引用
export abstract class ComponentRef<C> {
/**
* Location of the Host Element of this Component Instance.
*/
abstract get location(): ElementRef;
然后將它作為最后一個孩子插入document.body
標記中。 所以我們在頁面上看到它。
3)假設我們的ModalContent
具有靜態內容,還將執行一些交互操作。
@Component({
selector: 'modal-content',
template: `
I'm beeing opened as modal! {{ counter }}
<button (click)="counter = counter + 1">Increment</button>
`
})
export class ModalContent {
counter = 1;
}
如果我們刪除
this._appRef.attachView(contentCmpt.hostView);
那么我們的視圖將不會在更改檢測周期中進行更新,因為我們是通過ComponentFactory.create
創建的,並且該視圖不屬於更改檢測樹中任何項目的一部分(與通過ViewContainerRef.createComponent
創建的不同)。 Angular為此目的打開了API,我們可以輕松地將視圖添加到根views
https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428 ,然后我們的組件將被更新在Application.tick
期間https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.