您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關(guān)如何使用Angular CDK Portal創(chuàng)建動態(tài)內(nèi)容,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
Angular 官方提供了一套組件開發(fā)套件 Component Dev Kit (CDK),作為各種 Angular 組件開發(fā)的基礎(chǔ)工具,其中就提供 “Portal(傳送門)” 來輔助動態(tài)視圖的創(chuàng)建。
這個 ”動態(tài)視圖“ 可以是組件、TemplateRef 或者 DOM 元素,分別對應三種 Portal 類型(ComponentPortal、TemplatePortal、DomPortal)。它們?nèi)齻€的抽象泛型基類是 Portal<T>
,有三個方法:attach(掛載到容器)、detach(從容器移除)、isAttached(判斷視圖是否是掛載狀態(tài))。
而容器也是由一個抽象類 BasePortalOutlet
定義,和視圖類似,包含 attach(給容器掛載視圖)、detach(從容器移除視圖)、dispose(銷毀容器)、isAttached(是否有掛載視圖)。它的主要實現(xiàn)是 DomPortalOutlet
類。用以掛載三種類型的動態(tài)視圖。
先來看看三種動態(tài)視圖的創(chuàng)建。
ComponentPortal
相比原生 API,要創(chuàng)建一個動態(tài)組件非常的簡單,只需要把組件類傳入 ComponentPortal
構(gòu)造函數(shù)即可。
this.componentPortal = new ComponentPortal(ExampleComponent);
可以傳入任意自定義的組件類,用以創(chuàng)建 ComponentPortal
對象,再動態(tài)插入視圖中。
?注意:Angular 9 后的版本推薦使用 Ivy 編譯器,如果是老版本編譯器,傳入的組件類,需要在 Module 的 entryComponents
中聲明,并且這個 Module 不能懶加載。
TemplatePortal
TemplatePortal 的構(gòu)建,相比組件,多了一個參數(shù)(ViewContainerRef)??催^前一篇應該對它非常熟悉了,需要依賴它調(diào)用 createEmbeddedView()
來創(chuàng)建嵌入視圖。這里通過構(gòu)造注入,直接使用當前組件的 ViewContainerRef
實例。
<ng-template #testTemplate> <p>一些需要動態(tài)插入的內(nèi)容.</p> </ng-template>
@ViewChild('testTemplate') templatePortalContent: TemplateRef<any>; constructor(private _viewContainerRef: ViewContainerRef) { } ngAfterViewInit() { this.templatePortal = new TemplatePortal( this.templatePortalContent, this._viewContainerRef ); }
除了通過構(gòu)造函數(shù),TemplatePortal 也有一個指令(CdkPortal)可以便捷創(chuàng)建。
<ng-template cdkPortal> <p>一些需要動態(tài)插入的內(nèi)容.</p> </ng-template> <!-- 或?qū)懽?nbsp;--> <!-- 和上面寫法是一致的效果 --> <p *cdkPortal> 一些需要動態(tài)插入的內(nèi)容. </p>
然后通過 @ViewChild
就可以獲得 TemplatePortal
的實例了。
DomPortal
就像上面的示例通過 @ViewChild
獲取 Template 實例來創(chuàng)建,類似的也可以獲取 ElementRef 來創(chuàng)建動態(tài)的 DOM。
<div #domPortalContent><span>原生DOM內(nèi)容</span></div>
@ViewChild('domPortalContent') domPortalContent: ElementRef<HTMLElement>; ngAfterViewInit() { this.domPortal = new DomPortal(this.domPortalContent); }
可以動態(tài)的將這段 DOM 轉(zhuǎn)移到任意位置。要注意的是,轉(zhuǎn)移之后,原來的數(shù)據(jù)綁定,或者綁定的指令可能不會再繼續(xù)更新。
前面三種類型的 Portal 都說了可以渲染到任意位置,那具體怎么渲染呢?
CdkPortOutlet
最簡單的就是通過 CdkPortOutlet 指令了:
<div> <ng-template [cdkPortalOutlet]="anyPortal"></ng-template> </div>
給 anyPortal
傳值上面三個中任意的 Portal 實例,都會動態(tài)渲染到當前位置。
和原生 API 的指令不同,它可以自動判斷是什么類型的 Portal。另外,它還有個額外的事件:attached
,通過這個事件,可以獲取到掛載的組件實例,或者 TemplateRef。這也讓和掛載組件的交互變得十分方便了。
構(gòu)造容器實例
不過既然說了是可以渲染到任意位置,那自然也包括 Angular 應用外部,要渲染到應用之外,就需要咱們通過構(gòu)造函數(shù)創(chuàng)建容器實例。
這個容器類就是 DomPortalOutlet
,它是 PortalOutlet
的實現(xiàn)子類。它的構(gòu)造參數(shù)主要是:Element(掛載視圖的DOM節(jié)點)、ComponentFactoryResolver(和上篇一樣,用以動態(tài)構(gòu)建組件)、appRef(當前 Angular 應用的整體實例)、Injector(注入器,用于傳遞依賴)。
constructor( private viewContainerRef: ViewContainerRef, @Inject(DOCUMENT) private document: any, private injector: Injector, private componentFactoryResolver: ComponentFactoryResolver ) { // 在<body>下創(chuàng)建外部宿主元素 const container = this.document.createElement('div'); container.classList.add('outside-portal-container'); this.outsideContainer = this.document.body.appendChild(container); // 獲取應用實例 this.appRef = this.injector.get(ApplicationRef); // 創(chuàng)建外部容器 this.outsideOutlet = new DomPortalOutlet( this.outsideContainer, this.componentFactoryResolver, this.appRef, this.injector ); } // 在應用外部插入動態(tài)組件。 openComponentPortalOutSideAngularContext(): void { const componentPortal = new ComponentPortal(AlertComponent); const componentRef = this.outsideOutlet.attach(componentPortal); componentRef.instance.closeAlert.subscribe(() => { this.outsideOutlet.detach(); }); } // 在應用外部插入動態(tài)模板。 openTemplatePortalInsideAngularContext(): void { const templatePortal = new TemplatePortal(this.templatePortalContent, this.viewContainerRef); this.outsideOutlet.attach(templatePortal); }
除了掛載視圖到應用外的 DOM 元素中,還需要能夠跟視圖進行數(shù)據(jù)交互,組件可以通過注入依賴,模板可以傳入上下文對象。
const injectionToken = new InjectionToken<any>('Sharing data with outside component portal'); const customInjector = Injector.create({ providers: [{ provide: CustomInjectionToken, useValue: 'test value' }] });
對創(chuàng)建 outsideContainer 的代碼稍作修改,把這個 customInjector 作為參數(shù)傳入(而不是使用當前組件的 injector)
// 重點是第四個參數(shù) new DomPortalOutlet(this.outsideContainer, this.componentFactoryResolver, this.appRef, customInjector);
相應的,這個組件只需要按這個 injectionToken 注入依賴即可:
constructor(@Inject(injectionToken) public customData: any) {}
給模板傳遞上下文就比較簡單了,在創(chuàng)建 TemplatePortal 對象時,傳入上下文對象即可:
// 重點是第三個參數(shù) new TemplatePortal(this.templatePortalContent, this.viewContainerRef, { customData:'test values' });
關(guān)于“如何使用Angular CDK Portal創(chuàng)建動態(tài)內(nèi)容”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。