iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
2
Modern Web

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

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

前言回顧

今天我們繼續介紹一個新元件 Select,當提供給使用者可選資料較多時的使用場景較多,比如省市區聯動選擇、後臺賬號管理等等。

元件

開發之前

我們仍然會以昨天的分析流程進行 Select 元件的介紹,在這幾天元件原始碼介紹過程中,我們也會逐步選擇難度上升的元件進行說明,循序漸進。

Select 元件

功能分析

select 功能元件想必大家在學習前端時都已經接觸過了,最基礎的選擇元件至少包含顯示、下拉選項兩部分:

<select>
  <option value ="material">Material</option>
  <option value ="ng-zorro">NG-ZORRO</option>
</select>

而對於 Angular 設計而言,我們需要 select 元件支援雙向繫結、事件觸發、自定義文字等多種功能,那麼我們帶著這些需求繼續進行。

程式碼實現

如何設計

html 原生使用方式給了我們很好的設計提示,就是將 option 選項抽離出來單獨維護,那麼我們想象中的選擇元件應該是和原生使用方式類似的:

<nz-select>
  <nz-option nzValue="material">Material</nz-option>
  <nz-option nzValue="ng-zorro">NG-ZORRO</nz-option>
</nz-select>

我們查閱 NG-ZORRO 的 Select 元件文件發現實際上的確是這樣使用的,但是它提供了一個屬性 nzLabel 來直接渲染顯示文字資訊,所以我們可以這樣使用:

<nz-select>
  <nz-option nzValue="material" nzLabel="Material"></nz-option>
  <nz-option nzValue="ng-zorro" nzLabel="NG-ZORRO"></nz-option>
</nz-select>

元件檔案結構

我們看一下 Select 元件的原始碼檔案結構:

components/select
├── demo
├── doc
├── style
├── index.ts
├── nz-option-container.component.html
├── nz-option-container.component.ts
├── nz-option-container.spec.ts
├── nz-option-group.component.html
├── nz-option-group.component.ts
├── nz-option-li.component.html
├── nz-option-li.component.ts
├── nz-option-li.spec.ts
├── nz-option.component.html
├── nz-option.component.ts
├── nz-option.pipe.spec.ts
├── nz-option.pipe.ts
├── nz-select-top-control.component.html
├── nz-select-top-control.component.ts
├── nz-select-top-control.spec.ts
├── nz-select-unselectable.directive.ts
├── nz-select-unselectable.spec.ts
├── nz-select.component.html
├── nz-select.component.spec.ts
├── nz-select.component.ts
├── nz-select.module.ts
├── nz-select.service.spec.ts
├── nz-select.service.ts
├── package.json
└── public-api.ts

看起來非常多的檔案,但是我們現在只需要關注這幾個檔案 nz-select.component.*nz-option.component.*,和我們之前想的一樣,NG-ZORRO 的選擇元件設計和原生 select 元素是一致的。

程式碼設計

那麼如果讓我們用 Angular 實現最簡單的 select 元件,我們按照上述設計該怎麼實現呢?(以下為模擬設計,非 NG-ZORRO 原始碼)

@Component({
  selector: 'nz-select',
  template: '
  	<!-- 顯示選中的專案 -->
	<div>
  		{{selectedOption.nzLabel}}
	</div>
  	<!-- 投射渲染 nz-option -->
	<ng-template>
	  <ul>
		   <ng-content></ng-content>
	  </ul>
	</ng-template>
  ',
})
export class NzInputComponent {
  @ContentChildren(NzOptionComponent) listOfNzOptionComponent: QueryList<NzOptionComponent>;
}
  
@Component({
  selector: 'nz-option',
  template: '
	 <li>{{nzOption.nzLabel}}</li>
  ',
})
export class NzOptionComponent {}

我們仍然使用了 ng-content 進行內容投射,在 NzInputComponent 裡可以通過 ContentChildren 獲取全部選項,當然我們可以通過這個在後面進行更多操作。

原始碼查閱

我們對照原始碼分解一下現有的元件,看一下 NG-ZORRO 是如何去設計這個元件的(以下程式碼可能存在部分刪除,重點在於結構性說明):

nz-select.component.html

<div cdkOverlayOrigin
  nz-select-top-control
  class="ant-select-selection">
</div>
<ng-template
  cdkConnectedOverlay
  nzConnectedOverlay
  [cdkConnectedOverlayHasBackdrop]="true"
  [cdkConnectedOverlayMinWidth]="nzDropdownMatchSelectWidth? null : triggerWidth"
  [cdkConnectedOverlayWidth]="nzDropdownMatchSelectWidth? triggerWidth : null"
  [cdkConnectedOverlayOrigin]="cdkOverlayOrigin"
  (backdropClick)="closeDropDown()"
  (detach)="closeDropDown();"
  (positionChange)="onPositionChange($event)"
  [cdkConnectedOverlayOpen]="open">
  <div
    class="ant-select-dropdown">
    <div nz-option-container
      style="overflow: auto;transform: translateZ(0px);"
      (keydown)="onKeyDown($event)"
      [nzMenuItemSelectedIcon]="nzMenuItemSelectedIcon"
      [nzNotFoundContent]="nzNotFoundContent"
      (nzScrollToBottom)="nzScrollToBottom.emit()">
    </div>
    <ng-template [ngTemplateOutlet]="nzDropdownRender"></ng-template>
  </div>
</ng-template>
<!--can not use ViewChild since it will match sub options in option group -->
<ng-template>
  <ng-content></ng-content>
</ng-template>

我們可以看到,Select 元件被拆分成了多個部分:

  • nz-select-top-control
  • nz-option-container
  • nz-option

https://img.alicdn.com/tfs/TB1dmYWhbj1gK0jSZFOXXc7GpXa-688-240.jpg

CDK Overlay

我們在上面看到,點選彈出下拉選項使用的是 cdk overlay 來建立一個浮動面板,來渲染下拉列表。事實上,在所有涉及到彈出層的元件中(Modal / Drawer / Message 等等)均採用了 cdk overlay 來建立彈出層,overlay 的使用方法也是十分簡單的:

const overlayRef = overlay.create({
  height: '400px',
  width: '600px',
});
const userProfilePortal = new ComponentPortal(UserProfile);
overlayRef.attach(userProfilePortal);

select 元件中,我們發現使用了 cdk 的 CdkOverlayOrigincdkConnectedOverlay 指令:

  • CdkOverlayOrigin:指令應用於元素,以使其可以使用 ConnectedPositionStrategy 用作overlay的位置源。
  • CdkConnectedOverlay:CdkConnectedOverlay指令的嵌入元素(通常使用ng-template載入)表明該嵌入元素是一個overlay。

更多 cdk 相關的內容,大家可以去 Material CDK 官方網站檢視。

總結 & 預告

我們今天暫時先介紹 nz-select 元件的基本設計結構,讓我們對整個元件設計有個大概的理解,當然其中具體的設計和資料同步我們還未涉及,在明天我們會繼續介紹選項列表渲染和資料同步的相關知識,大家也可以提前思考一下。

相關資源

  • Select 元件:https://ng.ant.design/components/select/zh
  • CDK:https://material.angular.io/cdk/overlay/overview

上一篇
[Angular 元件庫 NG-ZORRO 基礎入門] Day 24 - 原始碼初窺: Input
下一篇
[Angular 元件庫 NG-ZORRO 基礎入門] Day 26 - 原始碼初窺: Select - Part 2
系列文
Angular 元件庫 NG-ZORRO 基礎入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言