簡體   English   中英

Angular-服務注入動態組件?

[英]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>
  `,
})

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

在此處輸入圖片說明

我已經知道contentCmpFactorywindowCmpFactory是什么( 第#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.

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