利用Angular動態元件,製作高彈性客製化頁面! (下)

利用 Angular 動態元件,製作高彈性客製化頁面! (下)

前言

接續上一篇利用Angular動態元件,製作高彈性客製化頁面! (上)
我們完成了 在頁面上自由動態產生 component 的功能
這一篇則會接下去帶大家完成

  • 刪除已經產生出的元件
  • 在創立 component 時,傳入 Input 資料

讓我們的功能更完整,更貼近實戰使用!

核心知識會用到: ComponentRef

成果如下

https://huskylin.github.io/dynamic-component-demo-2/


刪除已經產生出的元件

概念

要刪除動態產生的元件,首先我們得知道頁面上的眾多元件中
我們如何找出要刪除的那一個?
概念是這樣

  1. 動態創立元件時,給予一個唯一值,記錄在該元件內
  2. 我們對元件按下刪除件的時候,利用 Output ,發送出該元件的唯一值
  3. 獲得唯一值後,在對應的 ComponentRef 中,利用 destroy 方法移除元件

程式碼

第一步,動態創立元件時,給予一個唯一值,記錄在該元件內

custom.component.ts

createComponent(component) {
      ...
      // 記錄 component 唯一值
      wrapperRef.instance['uniqueKey'] = ++this.childUniqueKey;
      // 儲存 wrapper 跟 target 的 ref,之後更新資料、刪除元件時會用
      this.customService.pushWrapperRefs(wrapperRef);
      this.customService.pushChartRefs(targetRef);
      // 如果開著刪除模式,新增的元件也要開啟刪除模式
      this.customService.refreshRemoveMode(this.removeable);
      // 訂閱刪除事件
      const self = this;
      wrapperRef.instance['remove'].subscribe(componentIdx => {
        self.customService.removeComponent(componentIdx);
      });
}

解說:

  1. 這邊的 wrapperRef.instance[‘uniqueKey’]
    會在該元件內創建一個名為uniqueKey的屬性 並且儲存傳進去的值
    就跟平時在寫 component 時

     export class MyComponent {
         ...
         uniqueKey = 123
     }

    是一樣的意思

  2. customService.pushWrapperRefs、customService.pushChartRefs
    則是將 ComponentRef 儲存到 service 的陣列中,方便日後做刪除的操作

  3. 訂閱刪除事件
    別忘了訂閱元件的 EventEmitter ,這樣按下元件刪除鍵時才會收到動作


第二步,我們對元件按下刪除件的時候,利用 Output ,發送出該元件的唯一值

small.component.ts

@Input() uniqueKey: number;
@Output() remove: EventEmitter<number> = new EventEmitter();

removeComponent() {
    this.remove.emit(this.uniqueKey);
  }

第三步,獲得唯一值後,在對應的 ComponentRef 中,利用 destroy 方法移除元件

custom.service.ts

removeComponent(uniqueKey) {
    const idx = this.wrapperRefs.findIndex(e => e.instance.uniqueKey === uniqueKey)
    this.wrapperRefs[idx].destroy();
    this.wrapperRefs = this.wrapperRefs.filter(e => e.instance.uniqueKey !== uniqueKey);
  }

Angular 的 ComponentRef 提供了一個 destroy 的方法

Destroys the component instance and all of the data structures associated with it.

wrapperRefs 是一個儲存了我們所創立的元件的陣列
根據唯一值找出我們需要刪除的元件,並且呼叫 destroy 方法
就完成刪除的功能了~


在創立 component 時,傳入 Input 資料

我們的元件,通常也會需要傳入參數
畢竟元件通常是把功能抽出來
再把不同資料傳入後呈現不同的畫面

那動態產生元件時要怎麼傳入資料呢
答案也是使用 ComponentRef.instance
程式碼如下:

updateInputData(component, targetRef) {
    // 產生 InputData
    const inputData = this.utilsService.getData(component.name, year);
    // 傳入到 Input 中,會觸發在 ngOnInit
    targetRef.instance['data'] = inputData;
    // 產生 change
    const changes = {
      data: new SimpleChange(undefined, inputData, false)
    };
    // 傳入到 Onchange
    if (typeof targetRef.instance.ngOnChanges !== 'undefined') {
      targetRef.instance.ngOnChanges(changes);
    }
}

解說:

  1. targetRef.instance[‘data’] = inputData;
    會傳值到元件的 @Input() data
    注意這邊會觸發的生命週期是 OnInit 階段
  2. 如果想要觸發 OnChanges,就可以看後半段的程式碼
    先產生一個 change ,再透過 targetRef.instance.ngOnChanges(changes) 來觸發

這樣就完成資料的傳值功能了~


成果展示

刪除元件

開啟刪除模式後
這邊用了個類似手機APP移除的視覺效果
(UX方面,滑鼠移上去的時候再停止動畫,避免抖到點不到XD)
並且透過不同的 class name 達成不同的抖動延時
點擊按鈕後就會觸發 EventEmitter ,並且透過 ComponentRef.destroy() 刪除

透過動態元件呈現交叉分析

這次我們在每個圖塊多加上了自己的時間篩選
以呈現動態元件的優點:
使用者可以高度客製化希望看到的頁面
另外在創立元件的選單上方,也加入了年份選擇
以呈現同樣的元件可以透過不同的 Input 資料
畫出不同的圖表內容

這次展示的內容都只是提供大家一個概念
實際上可以應用的場景當然不限於圖表、數據呈現
DEMO 連結在這邊,歡迎上去玩玩看
https://huskylin.github.io/dynamic-component-demo-2/

完整程式碼在 Github 專案

真心覺得 Angular 的這個功能超酷
終於打完這篇的分享啦,灑花~