iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 27
1
Modern Web

Angular 元件庫 NG-ZORRO 基礎入門系列 第 27

[Angular 元件庫 NG-ZORRO 基礎入門] Day 27 - 原始碼初窺: Message

前言回顧

今天我們要閱讀的是 Message 元件,我們在之前介紹過,NG-ZORRO 的很多包含彈出層的元件均結合了 Material CDK Overlay 實現,我們就以這個簡單元件來看看如何使用 cdk overlay 來建立自定義元件。

元件

元件使用

import { Component } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd/message';

@Component({
  selector: 'nz-demo-message-info',
  template: `
    <button nz-button [nzType]="'primary'" (click)="createBasicMessage()">Display normal message</button>
  `
})
export class NzDemoMessageInfoComponent {
  constructor(private message: NzMessageService) {}
  createBasicMessage(): void {
    this.message.info('This is a normal message');
  }
}

點選按鈕 會在全域性螢幕上彈出一個 message
https://img.alicdn.com/tfs/TB1VBHShq67gK0jSZFHXXa9jVXa-1286-204.gif

如何實現

cdk overlay

我們之前介紹過,cdk overlay 可以通過簡單的方法建立一個浮層:

constructor(private overlay: Overlay) {
  this.createOverlay();
}
...
const overlayRef = overlay.create({
  height: '400px',
  width: '600px',
});
const userProfilePortal = new ComponentPortal(UserProfile); // 預設模板
overlayRef.attach(userProfilePortal);

這樣,cdk 會幫助我們建立一個 400 x 600 的浮層,那麼 Message 元件是不是這樣實現的呢?

NzMessageService

我們先看一下 nz-message.service.ts 檔案:

export class NzMessageService extends NzMessageBaseService<...> {
  ...
  // Shortcut methods
  success(content: string | TemplateRef<void>, options?: NzMessageDataOptions): NzMessageDataFilled {
    return this.createMessage({ type: 'success', content }, options);
  }
  create(
    type: 'success' | 'info' | 'warning' | 'error' | 'loading' | string,
    content: string | TemplateRef<void>,
    options?: NzMessageDataOptions
  ): NzMessageDataFilled {
    return this.createMessage({ type, content }, options);
  }
}

這個檔案繼承 NzMessageBaseService(nz-message-base.service.ts),方法均呼叫 createMessage 方法,那麼我們繼續追溯,找到了它的實現方法:

export class NzMessageBaseService<
  ContainerClass extends NzMessageContainerComponent,
  ...
> {
  protected _container: ContainerClass;
  constructor(...) {
    this._container = this.withContainer();
  }

  // Manually creating container for overlay to avoid multi-checking error, see: https://github.com/NG-ZORRO/ng-zorro-antd/issues/391
  // NOTE: we never clean up the container component and it's overlay resources, if we should, we need to do it by our own codes.
  private withContainer(): ContainerClass {
    const containerInstance = this.nzSingletonService.getSingletonWithKey(this.name);

    if (containerInstance) {
      return containerInstance as ContainerClass;
    }

	// 關注一下這裡
    const factory = this.cfr.resolveComponentFactory(this.containerClass);
    const componentRef = factory.create(this.injector); // Use root injector
    componentRef.changeDetectorRef.detectChanges(); // Immediately change detection to avoid multi-checking error
    this.appRef.attachView(componentRef.hostView); // Load view into app root
    const overlayPane = this.overlay.create().overlayElement;
    overlayPane.style.zIndex = '1010'; // Patching: assign the same zIndex of ant-message to it's parent overlay panel, to the ant-message's zindex work.
    overlayPane.appendChild((componentRef.hostView as EmbeddedViewRef<{}>).rootNodes[0] as HTMLElement);
    return componentRef.instance;
  }
}

看一下 create 部分,可以看到它建立了一個 overlay 浮層,然後返回 ComponentRef(NzMessageContainerComponent) 例項。

它也是通過掛載到 root view 來渲染的,這樣彈出層預設 append 到頁面底部建立一個新的 div container
https://img.alicdn.com/tfs/TB10IbShpT7gK0jSZFpXXaTkpXa-1364-402.jpg

所以如果我們要修改 message 樣式需要到 styles.less 檔案中去修改,不能再呼叫其服務的元件裡修改。如果遇到使用問題沒辦法解決也可以通過這種回溯方式,嘗試檢視是否是元件的 Bug,從而選擇 提 Issue 或者 提 PR 去修復它。

其他元件

還有其他元件諸如 ModalDrawer 等元件,彈出層模式基本都是這種思路,通過 overlay.create() 建立浮層,當然如果你需要在關閉浮層時返回一些資料,可以去監聽 OverlayRef.backdropClick 事件,參考我們之前介紹的 Select 元件(31行)

總結 & 預告

我們今天僅以一個典型的浮層元件,介紹了它是如何工作的,更復雜的如 Modal 元件包含了更多響應事件,大家可以嘗試參考去實現,甚至可以按照這種方式搭建自己的浮層元件,結合 cdk 拖放 功能也可以實現可拖拽調整位置的浮層,就像 webstorm 等開發工具的搜尋結果框一樣。

相關資料


上一篇
[Angular 元件庫 NG-ZORRO 基礎入門] Day 26 - 原始碼初窺: Select - Part 2
下一篇
[Angular 元件庫 NG-ZORRO 基礎入門] Day 28 - 原始碼初窺: core
系列文
Angular 元件庫 NG-ZORRO 基礎入門30

尚未有邦友留言

立即登入留言