2017-06-13 122 views
1

我有經由到HTML服務注入任何成分的工作代碼:Angular - 服務注入動態組件?

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 。:

/*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*/ } 

App.ts:

@Component({ 
    selector: 'my-app', 
    template: ` 
    <button (click)="open()">Open modal window</button> 
    `, 
}) 

結果(點擊一個按鈕調用該服務的方法時):

enter image description here

我已經知道contentCmpFactorywindowCmpFactory是(行#8,9

但是我不認爲以後會發生什麼。關於第#11,第12行 - 文檔說「創建了一個新組件」。

問題:

1 - 線#12:什麼是[[contentCmpt.location.nativeElement]]辦? (的文檔說,它的類型是projectableNodes?: any[][] - 這是什麼意思?)

2 - 線#14:什麼是[[windowCmpt.location.nativeElement]]辦?

3行#16,#17:如果我已經做了appendChild,那麼爲什麼需要它們? (docs說:附加一個視圖,以便它會被檢查髒。 - 所以?)。

PLUNKER

回答

4

數目:

1)角取ComponentFactory並用給定的元件噴射器創建組件實例,並與可投影節點

windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]); 

1.1元噴射器的陣列將會使用角度爲時

const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR); 

這裏還簡單地說明了應用程序的依賴性解析算法,沒有延遲加載。使用延遲加載它看起來更復雜。

enter image description here

欲瞭解更多詳情,請參閱設計文檔element injector vs module injector

1.2可放映節點是節點的元素,這是「預測」(transcluded)中,我們已經在我們的組件的模板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組件的主機元素獲取參考(contentCmpt.location)。這是modal-content元素。然後角將會做所有的魔術投影在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]]); 

總之Why is projectableNodes an any[][]?

2)在接下來的線

document.querySelector('body').appendChild(windowCmpt.location.nativeElement); 

我們正在參考創建內存modal-window元素。 ComponentRef使我們能夠做到這一點,因爲它在location吸氣存儲參考host元素

export abstract class ComponentRef<C> { 
    /** 
    * Location of the Host Element of this Component Instance. 
    */ 
    abstract get location(): ElementRef; 

,然後在document.body標籤的最後一個子inseting它。所以我們在頁面上看到它。

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)。角度開放的API用於這樣的目的,我們可以很容易地將視圖添加到根viewshttps://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428和之後,我們的組件將在Application.tickhttps://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558

+0

♡♡一如既往....謝謝。多好的答案。 –

+0

@yuruzi,但如果我有兩個(名爲'ng-content select = ...')ng內容,我該如何映射每個部分('header'到第一個ng-content,'body'到第二個ng-content)? https://plnkr.co/edit/ozjVRwM3VMzNVErbH92O?p=preview –

+0

'[contentCmpt.location.nativeElement.children]'https://plnkr.co/edit/UnahRZEVa0diSxnKH3D8?p=preview – yurzui