我們昨天已經瞭解了 NG-ZORRO 專案的基本結構,也介紹了一些核心檔案資訊,今天我們來探索一下 NG-ZORRO 元件設計裡的一些設計方法。
我們都知道,開發一個元件絕不是簡單的程式碼堆砌,我們要對元件進行充分分析,包括功能分析、質量保證、文件說明、國際化、維護擴充套件等。
我們今天就以 Input
元件來介紹一下如何從開發到釋出的過程。
作為頁面開發中最常用的元件,我們在多種場景中都會使用得到。
我們先來看一下 html 原生輸入框 input 的例子:
<input type="text" name="name" placeholder="this is an input." />
我們查閱 MDN 介紹文件也發現 input
元素包含了非常多的屬性供我們使用。不知道大家是否還記得我們專案剛開始時介紹 button
元件設計模式時,為什麼不採用 component
包裹的形式開發,例如這樣在 Angular 中實現 input 元件:
@Component({
selector: 'nz-input',
template: '<input xxx="xxx" />',
})
export class NzInputComponent {
@Input() type: string;
...
}
很明顯,這種方式極大地限制了我們對 input
元素的使用和拓展,比如我們如果想未來支援 無障礙 閱讀,就會無從下手。
那麼是否可以使用 ng-content
來投射渲染使用者手動輸入的元素呢?比如這樣設計:
@Component({
selector: 'nz-input',
template: '<span #inputElement><ng-content></ng-content></span>',
})
export class NzInputComponent {
@ViewChild('inputElement', { static: true }) contentElement: ElementRef;
@Input() property: propertyType;
// do something to deal with inputElement
}
這時我們可以這樣使用 input
元件:
<nz-input [nzProperty]="propertyValue">
<input type="text" name="name" placeholder="this is an input." />
</nz-input>
將使用者的 input
元素渲染,nz-input
只作為增強屬性,比如新增主題色、樣式等。這種方式似乎已經滿足我們的需求了,但是每次只是使用 input
元素就得 nz-input
包裹就顯得太冗餘了,於是 Directive
指令成為了更適合的選擇。
@Directive({
selector: '[nz-input]',
exportAs: 'nzInput',
host: {
'[class.ant-input-disabled]': 'disabled',
'[class.ant-input-lg]': `nzSize === 'large'`,
'[class.ant-input-sm]': `nzSize === 'small'`
}
})
export class NzInputDirective {
@Input() nzSize: NzSizeLDSType = 'default';
@Input() @InputBoolean() disabled = false;
constructor(renderer: Renderer2, elementRef: ElementRef) {
renderer.addClass(elementRef.nativeElement, 'ant-input');
}
}
這就是 NG-ZORRO 的 input
指令元件,接受了大小、禁用屬性來增強 input
元素。非常簡單的程式碼實現,在設計中如果是對已有的 html 元素進行增強,儘量要做到非侵入,讓使用者有更多的操作權,不能取小舍大。
同樣的,nz-autosize.directive.ts
針對文字域多行輸入也遵循了此設計理念。
但是有時候我們需要對實現如下功能:
<nz-input-group [nzSuffix]="suffixTemplate" [nzPrefix]="prefixTemplate">
<input type="text" nz-input placeholder="Enter your username" />
</nz-input-group>
<ng-template #prefixTemplate><i nz-icon nzType="user"></i></ng-template>
<ng-template #suffixTemplate
><i nz-icon nz-tooltip nzTitle="Extra information" nzType="info-circle"></i
></ng-template>
這對 input
進行深度定製,簡單的指令已無法滿足了,這時我們就可以使用上述說過的 ng-content
方法來增強,看一下 nz-input-group 程式碼,我們發現其設計都用了 ng-content
渲染使用者資料,在外部增加自有增強屬性,如支援字首字尾的程式碼:
<span class="ant-input-affix-wrapper" [class.ant-input-affix-wrapper-sm]="isSmall" [class.ant-input-affix-wrapper-lg]="isLarge" *ngIf="isAffix">
<ng-template *ngTemplateOutlet="affixTemplate"></ng-template>
</span>
<!--前後綴模板-->
<ng-template #affixTemplate>
<!--字首-->
<span class="ant-input-prefix" *ngIf="nzPrefix || nzPrefixIcon">
<!-- TODO: should have a class to set its color, cc: antd-->
<i nz-icon [nzType]="nzPrefixIcon" *ngIf="nzPrefixIcon" style="color: rgba(0, 0, 0, 0.25)"></i>
<ng-container *nzStringTemplateOutlet="nzPrefix">{{ nzPrefix }}</ng-container>
</span>
<!--渲染使用者元素-->
<ng-template *ngTemplateOutlet="contentTemplate"></ng-template>
<!--字尾-->
<span class="ant-input-suffix" *ngIf="nzSuffix || nzSuffixIcon">
<i nz-icon [nzType]="nzSuffixIcon" *ngIf="nzSuffixIcon"></i>
<ng-container *nzStringTemplateOutlet="nzSuffix">{{ nzSuffix }}</ng-container>
</span>
</ng-template>
<!--使用者輸入元素-->
<ng-template #contentTemplate>
<ng-content></ng-content>
</ng-template>
這樣我們即可以對使用者元素進行保留,也能在此基礎上渲染我們需要新增的屬性。
因為是開源元件,所以我們必須要建立一個測試檔案來測試我們支援的全部屬性,Angular 測試 內容可在官方網站查閱到。目前元件庫的測試覆蓋率達到 92%+,我們在設計元件時(如果是通用性元件,非業務元件),最好形成良好的測試習慣,保證元件可用性。
今天我們介紹了 input
元件的原始碼,主要注意的是 directive
指令和 ng-content
的使用,在設計元件時大家也需要權衡哪種方式是最適合自己的。