首頁 > web前端 > js教程 > 主體

手把手教你使用Angular CDK Portal創建動態內容

青灯夜游
發布: 2021-08-25 09:49:18
轉載
2712 人瀏覽過

手把手教你使用Angular CDK Portal創建動態內容

先前介紹過原生 API 的動態視圖插入,功能上已經可以滿足大多數使用場景。不過也有一些缺憾,還沒有解決在 Angular 應用程式外插入內容的需求,指令也不能跟動態插入的元件有輸入輸出的互動。

好在Angular 官方提供了一套組件開發套件Component Dev Kit (CDK),作為各種Angular 組件開發的基礎工具,其中就提供“Portal(傳送門)” 來輔助動態視圖的創建。

這個 」動態視圖「可以是元件、TemplateRef 或 DOM 元素,分別對應三種 Portal 類型(ComponentPortal、TemplatePortal、DomPortal)。它們三個的抽象泛型基底類別是Portal<T>,有三個方法:attach(掛載到容器)、detach(從容器移除)、isAttached(判斷視圖是否為掛載狀態)。

而容器也是由一個抽象類別BasePortalOutlet 定義,和視圖類似,包含attach(給容器掛載視圖)、detach(從容器移除視圖)、dispose(銷毀容器) 、isAttached(是否有掛載視圖)。它的主要實作是 DomPortalOutlet 類別。用以掛載三種類型的動態視圖。

建立動態內容

先來看看三種動態視圖的建立。

ComponentPortal

比較原生API,要建立一個動態元件非常的簡單,只要要把元件類別傳入ComponentPortal 建構子即可。

this.componentPortal = new ComponentPortal(ExampleComponent);
登入後複製

可以傳入任意自訂的元件類,用以建立 ComponentPortal 對象,再動態插入視圖中。

✨注意:Angular 9 後的版本建議使用Ivy 編譯器,如果是舊版編譯器,傳入的元件類,需要在Module 的entryComponents 中聲明,並且這個Module不能懶加載。

TemplatePortal

TemplatePortal 的構建,相較於元件,多了一個參數(ViewContainerRef)。看過前一篇應該對它非常熟悉了,需要依賴它呼叫 createEmbeddedView() 來建立嵌入視圖。這裡透過建構注入,直接使用目前元件的 ViewContainerRef 實例。

<ng-template #testTemplate>
  <p>一些需要动态插入的内容.</p>
</ng-template>
登入後複製
@ViewChild(&#39;testTemplate&#39;) templatePortalContent: TemplateRef<any>;

constructor(private _viewContainerRef: ViewContainerRef) { }

ngAfterViewInit() {
  this.templatePortal = new TemplatePortal(
    this.templatePortalContent,
    this._viewContainerRef
  );
}
登入後複製

除了透過建構函數,TemplatePortal 也有一個指令(CdkPortal)可以方便建立。

<ng-template cdkPortal>
  <p>一些需要动态插入的内容.</p>
</ng-template>

<!-- 或写作 -->

<!-- 和上面写法是一致的效果 -->
<p *cdkPortal>
  一些需要动态插入的内容.
</p>
登入後複製

然後透過 @ViewChild 就可以獲得 TemplatePortal 的實例了。

DomPortal

就像上面的範例透過@ViewChild 取得Template 實例來創建,類似的也可以獲得ElementRef 來建立動態的DOM。

<div #domPortalContent><span>原生DOM内容</span></div>
登入後複製
@ViewChild(&#39;domPortalContent&#39;) domPortalContent: ElementRef<HTMLElement>;
ngAfterViewInit() {
  this.domPortal = new DomPortal(this.domPortalContent);
}
登入後複製

可以動態的將這段 DOM 轉移到任意位置。要注意的是,轉移之後,原來的資料綁定,或者綁定的指令可能不會再繼續更新。

插入容器

前面三種類型的 Portal 都說了可以渲染到任何位置,那要具體怎麼渲染呢?

CdkPortOutlet

最簡單的就是透過CdkPortOutlet 指令了:

<div>
  <ng-template [cdkPortalOutlet]="anyPortal"></ng-template>
</div>
登入後複製

#anyPortal 傳值上面三個中任意的Portal 實例,都會動態渲染到目前位置。

和原生 API 的指令不同,它可以自動判斷是什麼類型的 Portal。另外,它還有個額外的事件:attached,透過這個事件,可以取得到掛載的元件實例,或是 TemplateRef。這也讓和掛載組件的互動變得十分方便了。

建構容器實例

不過既然說了是可以渲染到任意位置,那自然也包括Angular 應用外部,要渲染到應用之外,就需要咱們透過建構函式創建容器實例。

這個容器類別就是 DomPortalOutlet,它是 PortalOutlet 的實作子類別。它的建構參數主要是:Element(掛載視圖的DOM節點)、ComponentFactoryResolver(和上篇一樣,用以動態建構元件)、appRef(目前Angular 應用的整體實例)、Injector(注入器,用於傳遞依賴)。

constructor(
  private viewContainerRef: ViewContainerRef,
  @Inject(DOCUMENT) private document: any,
  private injector: Injector,
  private componentFactoryResolver: ComponentFactoryResolver
) {
  // 在<body>下创建外部宿主元素
  const container = this.document.createElement(&#39;div&#39;);
  container.classList.add(&#39;outside-portal-container&#39;);
  this.outsideContainer = this.document.body.appendChild(container);
  // 获取应用实例
  this.appRef = this.injector.get(ApplicationRef);
  // 创建外部容器
  this.outsideOutlet = new DomPortalOutlet(
    this.outsideContainer, 
    this.componentFactoryResolver, 
    this.appRef, 
    this.injector
  );
}

// 在应用外部插入动态组件。
openComponentPortalOutSideAngularContext(): void {
  const componentPortal = new ComponentPortal(AlertComponent);
  const componentRef = this.outsideOutlet.attach(componentPortal);
    componentRef.instance.closeAlert.subscribe(() => {
      this.outsideOutlet.detach();
    });
}

// 在应用外部插入动态模板。
openTemplatePortalInsideAngularContext(): void {
  const templatePortal = new TemplatePortal(this.templatePortalContent, this.viewContainerRef);
  this.outsideOutlet.attach(templatePortal);
}
登入後複製

除了掛載視圖到應用程式外的 DOM 元素中,還需要能夠跟視圖進行資料交互,元件可以透過注入依賴,模板可以傳入上下文物件。

const injectionToken = new InjectionToken<any>(&#39;Sharing data with outside component portal&#39;);
const customInjector = Injector.create({ providers: [{ provide: CustomInjectionToken, useValue: &#39;test value&#39; }] });
登入後複製

對建立outsideContainer 的程式碼稍作修改,把這個customInjector 作為參數傳入(而不是使用目前元件的injector)

// 重点是第四个参数
new DomPortalOutlet(this.outsideContainer, this.componentFactoryResolver, this.appRef, customInjector);
登入後複製

對應的,這個元件只需要按這個injectionToken 注入依賴即可:

constructor(@Inject(injectionToken) public customData: any) {}
登入後複製

傳遞上下文給範本就比較簡單了,在建立TemplatePortal 物件時,傳入上下文物件即可:

// 重点是第三个参数
new TemplatePortal(this.templatePortalContent, this.viewContainerRef, { customData:&#39;test values&#39; });
登入後複製

總結

  • ################################################ ###比較原生API,CDK portal 主要實作了:############動態插入視圖到應用程式外部的能力;###
  • 和插入到外部的視圖資料互動的能力;

  • #更便捷和靈活的指令。

有了它,創建動態的元件容器,或是彈跳窗,浮動選單,甚至是建立一個低程式碼設計平台,都變得更加容易了。

專案原始碼:https://github.com/locotor/angular-dynamic-view-example

線上範例:https://coding-pages-bucket-1575455- 8137703-14801-541995-1303365836.cos-website.ap-beijing.myqcloud.com/

更多程式相關知識,請造訪:程式設計入門! !

以上是手把手教你使用Angular CDK Portal創建動態內容的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板