iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0

前言

上一篇使用 *ngComponentOutlet 的方式來動態產生元件,本篇要跟大家分享的是 ViewContainerRef,在程式中產生元件實體後再嵌入到畫面上,好處是畫面上的所有元件動作都可以交由程式來控管,配合複雜的業務邏輯可以同時產生多個元件嵌入到同一個 ViewContainer,而抽象類 ViewContainerRef 是 Angular 提供的,它包含許多方便的 API 讓開發者可以操作 DOM,接下來看一個實際的範例。


創建一個 directive 作為 loader

src\app\shared\directives\publicity-loader.directive.ts

import { Directive, ViewContainerRef } from '@angular/core';

/**
 * ## 廣告格式元件載入器
 *
 * @export
 * @class PublicityLoaderDirective
 */
@Directive({
    selector: '[appPublicityLoader]',
})
export class PublicityLoaderDirective {
    constructor(public viewContainerRef: ViewContainerRef) {}
}

載入 PublicityLoaderDirective 到 SharedModule

src\app\shared\shared.define.ts

// 共用指令
import { PublicityLoaderDirective } from './directives/publicity-loader.directive';

export const COMPONENTS = [
    ...
    PublicityLoaderDirective,
];

在 [PublicityComponent] 引用動態元件

使用 appPublicityLoader

src\app\publicity\publicity.component.html

<div class="publicity">
    <!-- 廣告編輯器 -->
    <nb-card class="editor">
        ...
        <div class="editor-body">
            <!-- viewContainerRef 動態載入 -->
            <ng-container appPublicityLoader></ng-container>
        </div>
        ...
    </nb-card>

    <!-- 結果預覽 -->
    <nb-card class="previewer" *ngIf="showResult">
        ...
    </nb-card>
</div>

在 PublicityComponent 使用 ComponentFactoryResolver

src\app\publicity\publicity.component.ts

import { Component, OnInit, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { TextComponent, LinkComponent, HtmlComponent } from '@shared/components';
import { PublicityLoaderDirective } from '@shared/directives/publicity-loader.directive';

/**
 * ## 廣告元件
 *
 * @export
 * @class PublicityComponent
 * @implements {OnInit}
 */
@Component({
    selector: 'app-publicity',
    templateUrl: './publicity.component.html',
    styleUrls: ['./publicity.component.scss'],
})
export class PublicityComponent implements OnInit {
    ...

    @ViewChild(PublicityLoaderDirective, { static: true }) appPublicityLoader: PublicityLoaderDirective;

    /**
     * ### 動態生成元件畫面
     *
     * @param {string} [type='']
     * @return {*}
     * @memberof PublicityComponent
     */
    loadComponent(type = '') {
        if (!type) return;

        const componentList: any = {
            text: TextComponent,
            link: LinkComponent,
            html: HtmlComponent,
        };

        const componentFactory = this.publicityFactoryResolver.resolveComponentFactory(componentList[type]);
        const viewContainerRef = this.appPublicityLoader.viewContainerRef;
        viewContainerRef.clear();

        // 動態建立元件
        viewContainerRef.createComponent(componentFactory);
    }

    ...

    ngOnInit(): void {
        this.publicityState = this.publicityService.publicityState$.subscribe((resp) => {
            this.editorContent = resp;
        });

        // 先載入文字廣告格式元件
        this.loadComponent('text');
    }

    ...
}

成果畫面

g12

與上一篇結果相同,差別在於三種廣告格式的元件都是動態生成後載入的。


廣告編輯元件需求變更

客戶不希望廣告文案拘泥於某種格式,希望能組合現有廣告格式,做多樣化的文案呈現。

ViewContainerRef 說明文件

刪除 viewContainerRef clear 方法

src\app\publicity\publicity.component.ts

    ...

    loadComponent(type = '') {
        if (!type) return;

        const componentList: any = {
            text: TextComponent,
            link: LinkComponent,
            html: HtmlComponent,
        };

        const componentFactory = this.publicityFactoryResolver.resolveComponentFactory(componentList[type]);
        const viewContainerRef = this.appPublicityLoader.viewContainerRef;

        // 不使用 clear 方法 viewContainerRef.clear();

        // 動態建立元件
        viewContainerRef.createComponent(componentFactory);
    }

    ...

成果畫面

g14

publicityState 在答案結果呈現的部份需要自行調整,本篇重點是動態建立元件。


結論

Angular 操作 DOM 抽象類包含 ElementRefTemplateRefViewContainerRef,其中 ElementRef 是元素,TemplateRef 是模板,而 ViewContainerRef 則是視圖的操作。Modern Web Framework 的開發通常需要支援多個平台的多種瀏覽器,透過 Angular 提供的這些方法來操作 DOM 將能滿足 APP 支援多瀏覽器的需求。

Angular 路由模組也是專案中很重要的部份,接下來要介紹路由延遲載入與路由守衛。


參考

Dynamic component loader

stackblitz

ComponentFactoryResolver

ViewContainerRef

Angular DOM


上一篇
共用元件的動態載入(1)
下一篇
Angular 路由模組
系列文
angular專案開發指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言