摘要:使用指令代替查詢每一個指令都可以在它的構造器中注入引用。讓我們聲明這樣一個指令我已經在構造器中添加了檢查代碼來保證視圖容器在指令實例化的時候是可用的。
原文:https://blog.angularindepth.c...
作者:Max Koretskyi
譯者:而井
【翻譯】教你如何在@ViewChild查詢之前獲取ViewContainerRef
在我最新的一篇關于動態組件實例化的文章《在Angular中關于動態組件你所需要知道的》中,我已經展示了如何將一個子組件動態地添加到父組件中的方法。所有動態的組件通過使用ViewContainerRef的引用被插入到指定的位置。這個引用通過指定一些模版引用變量來獲得,然后在組件中使用類似ViewChild的查詢來獲取它(模版引用變量)。
在此快速的復習一下。假設我們有一個父組件App,并且我們需要將子組件A插入到(父組件)模版的指定位置。在此我們會這么干。
組件A我們來創建組件A
@Component({
selector: "a-comp",
template: `
I am A component
`,
})
export class AComponent {
}
App根模塊
然后將(組件A)它在declarations和entryComponents中進行注冊:
@NgModule({ imports: [BrowserModule], declarations: [AppComponent, AComponent], entryComponents: [AComponent], bootstrap: [AppComponent] }) export class AppModule { }組件App
然后在父組件App中,我們添加創建組件A實例和插入它(到指定位置)的代碼。
@Component({ moduleId: module.id, selector: "my-app", template: `I am parent App component
`, }) export class AppComponent { @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef; constructor(private r: ComponentFactoryResolver) {} ngAfterViewInit() { const factory = this.r.resolveComponentFactory(AComponent); this.vc.createComponent(factory); } }
在plunker中有可以運行例子(譯者注:這個鏈接中的代碼已經無法運行,所以譯者把代碼整理了一下,放到了stackblitz上了,可以點擊查看預覽)。如果有什么你不能理解的,我建議你閱讀我一開始提到過的文章。
使用上述的方法是正確的,也可以運行,但是有一個限制:我們不得不等到ViewChild查詢執行后,那時正處于變更檢測期間。我們只能在ngAfterViewInit生命周期之后來訪問(ViewContainerRef的)引用。如果我們不想等到Angular運行完變更檢測之后,而是想在變更檢測之前擁有一個完整的組件視圖呢?我們唯一可以做到這一步的就是:用directive指令來代替模版引用和ViewChild查詢。
使用directive指令代替ViewChild查詢每一個指令都可以在它的構造器中注入ViewContainerRef引用。這個將是與一個視圖容器相關的引用,而且是指令的宿主元素的一個錨地。讓我們聲明這樣一個指令:
import { Directive, Inject, ViewContainerRef } from "@angular/core"; @Directive({ selector: "[app-component-container]", }) export class AppComponentContainer { constructor(vc: ViewContainerRef) { vc.constructor.name === "ViewContainerRef_"; // true } }
我已經在構造器中添加了檢查(代碼)來保證視圖容器在指令實例化的時候是可用的。現在我們需要在組件App的模版中使用它(指令)來代替#vc模版引用:
如果你運行它,你會看到它是可以運行的。好的,我們現在知道在變更檢查之前,指令是如何訪問視圖容器的了。現在我們需要做的就是把組件傳遞給它(指令)。我們要怎么做呢?一個指令可以注入一個父組件,并且直接調用(父)組件的方法。然而,這里有一個限制,就是組件不得不要知道父組件的名稱。或者使用這里描述的方法。
一個更好的選擇就是:用一個在組件及其子指令之間共享服務,并通過它來溝通!我們可以直接在組件中實現這個服務并將其本地化。為了簡化(這一操作),我也將使用定制的字符串token:
const AppComponentService= { createListeners: [], destroyListeners: [], onContainerCreated(fn) { this.createListeners.push(fn); }, onContainerDestroyed(fn) { this.destroyListeners.push(fn); }, registerContainer(container) { this.createListeners.forEach((fn) => { fn(container); }) }, destroyContainer(container) { this.destroyListeners.forEach((fn) => { fn(container); }) } }; @Component({ providers: [ { provide: "app-component-service", useValue: AppComponentService } ], ... }) export class AppComponent { }
這個服務簡單地實現了原始的發布/訂閱模式,并且當容器注冊后會通知訂閱者們。
現在我們可以將這個服務注入AppComponentContainer指令之中,并且注冊(指令相關的)視圖容器了:
export class AppComponentContainer { constructor(vc: ViewContainerRef, @Inject("app-component-service") shared) { shared.registerContainer(vc); } }
剩下唯一要做的事情就是當容器注冊時,在組件App中進行監聽,并且動態地創建一個組件了:
export class AppComponent { vc: ViewContainerRef; constructor(private r: ComponentFactoryResolver, @Inject("app-component-service") shared) { shared.onContainerCreated((container) => { this.vc = container; const factory = this.r.resolveComponentFactory(AComponent); this.vc.createComponent(factory); }); shared.onContainerDestroyed(() => { this.vc = undefined; }) } }
在plunker中有可以運行例子(譯者注:這個鏈接中的代碼已經無法運行,所以譯者把代碼整理了一下,放到了stackblitz上了,可以點擊查看預覽)。你可以看到,已經沒有ViewChild查詢(的代碼)了。如果你新增一個ngOnInit生命周期,你將看到組件A在它(ngOnInit生命周期)觸發前就已經渲染好了。
RouterOutlet也許你覺得這個辦法十分駭人聽聞,其實不是的,我們只需看看Angular中router-outlet指令的源代碼就好了。這個指令在構造器中注入了viewContainerRef,并且使用了一個叫parentContexts的共享服務在路由器配置中注冊自身(即:指令)和視圖容器:
export class RouterOutlet implements OnDestroy, OnInit { ... private name: string; constructor(parentContexts, private location: ViewContainerRef) { this.name = name || PRIMARY_OUTLET; parentContexts.onChildOutletCreated(this.name, this); ... }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100421.html
摘要:在探索抽象類前,先了解下如何在組件指令中獲取這些抽象類。下面示例描述在組建模板中如何創建如同其他抽象類一樣,通過屬性綁定元素,比如上例中,綁定的是會被渲染為注釋的元素,所以輸出也將是。你可以使用查詢模板引用變量來獲得抽象類。 原文鏈接:Exploring Angular DOM manipulation techniques using ViewContainerRef如果想深入學習 ...
摘要:這些依賴對象也進一步暴露了其設計思想。關鍵功能包括在上下文內掛載在上下文外掛載在上下文外共享數據。在構造必須依賴,所以可以直接創建嵌入視圖,然后手動強制執行變更檢測。提供了兩個指令和。 @angular/material 是 Angular 官方根據 Material Design 設計語言提供的 UI 庫,開發人員在開發 UI 庫時發現很多 UI 組件有著共同的邏輯,所以他們把這些共...
摘要:注意本文不是關于如何用編程的方式來創建組件的文章。在這個例子中,容器元素就是元素,模版將作為這個元素的兄弟節點被插入。用來演示以組件自身作為視圖容器,將組件中的模版插入視圖容器的效果。 原文鏈接:https://netbasal.com/angular-...作者:Netanel Basal譯者:而井 showImg(https://segmentfault.com/img/bVbl...
摘要:第一種方式是使用模塊加載器,如果你使用加載器的話,路由在加載子路由模塊時也是用的作為模塊加載器。還需注意的是,想要使用還需像這樣去注冊它你當然可以在里使用任何標識,不過路由模塊使用標識,所以最好也使用相同。 原文鏈接:Here is what you need to know about dynamic components in?Angular showImg(https://se...
摘要:翻譯在中操作意料之外的結果及優化技術原文鏈接作者譯者而井我最近在的一個研討會上討論了中的高級操作的話題。首先,我會介紹在中操作的工具和方法,然后再介紹一些我在研討會上沒有說過的更高級的優化技術。 【翻譯】在Angular中操作DOM:意料之外的結果及優化技術 原文鏈接:https://blog.angularindepth.c... 作者:Max Koretskyi 譯者:而井 ...
閱讀 2958·2021-11-08 13:20
閱讀 1031·2021-09-22 15:20
閱讀 660·2019-08-30 15:53
閱讀 1964·2019-08-30 15:43
閱讀 1278·2019-08-29 17:21
閱讀 540·2019-08-29 12:15
閱讀 2375·2019-08-28 17:51
閱讀 3142·2019-08-26 13:26