目前我們已經瞭解了 nz-select
元件的基本設計結構,我們昨天已經知道了 nz-select
被拆分為:nz-select-top-control
/ nz-option-container
/ nz-option
等部分,我們繼續來了解一下其實現過程。
我們先看一下 nz-select-top-control.component.ts,這個元件承擔了顯示已選專案的功能,我們檢視其程式碼發現 OnInit
中監聽了一些事件:
ngOnInit(): void {
// 監聽彈出層開啟事件,自動 focus
this.nzSelectService.open$.pipe(takeUntil(this.destroy$)).subscribe(open => {
if (this.inputElement && open) {
setTimeout(() => this.inputElement.nativeElement.focus());
}
});
// 監聽清除事件,針對 search 搜尋模式
this.nzSelectService.clearInput$.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.setInputValue('');
});
// 點選選項後手動觸發變化檢測,更新頁面
this.nzSelectService.check$.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.cdr.markForCheck();
});
}
已選專案的展示直接使用了 nzSelectService.listOfCachedSelectedOption
的相關值(單選多選表現形式不同,更多內容檢視原始碼):
<!--selected label-->
<div *ngIf="nzSelectService.listOfCachedSelectedOption.length && nzSelectService.listOfSelectedValue.length"
class="ant-select-selection-selected-value"
[attr.title]="nzSelectService.listOfCachedSelectedOption[0]?.nzLabel"
[ngStyle]="selectedValueStyle">
<ng-container *nzStringTemplateOutlet="nzCustomTemplate; context: { $implicit: nzSelectService.listOfCachedSelectedOption[0] }">
<ng-container>{{ nzSelectService.listOfCachedSelectedOption[0]?.nzLabel }}</ng-container>
</ng-container>
</div>
我們在之前介紹過,因為元件拆分後,各個元件間的資料互動如果全部使用 @Input
和 @Output
來管理,就稍顯複雜,使用 Service
則是更好的方案,結合 Rxjs 的 Subject
能夠在任意地方進行訂閱監聽,可以很好地做到資料同步。
大家可以檢視一下 nz-select.service.ts 發現,在 NzSelectService
中,很多事件都是通過 Rxjs 的 Subject 和 BehaviorSubject 實現,在不同元件中通過 subscribe
方法訂閱。
比如:
private listOfSelectedValueWithEmit$ = new BehaviorSubject<{ value: any[]; emit: boolean }>({
value: [],
emit: false
});
listOfSelectedValue$ = this.listOfSelectedValueWithEmit$.pipe(map(data => data.value));
// 在 nz-option-li.component.ts 中訂閱(簡略程式碼,詳細可前往Github檢視)
this.nzSelectService.listOfSelectedValue$.pipe(takeUntil(this.destroy$)).subscribe(list => {
this.selected = isNotNil(list.find(v => this.nzSelectService.compareWith(v, this.nzOption.nzValue)));
this.cdr.markForCheck();
});
這樣在合適的元件中進行監聽,可以做出相應的操作來改變樣式或 emit
事件。
在使用過程中,BehaviorSubject 和 Subject 有什麼不同呢?我們通過一個例子來簡單介紹一下:
const subject = new Subject();
subject.next('subject data');
subject.subscribe(x => console.log(x)); // no data
const behaviorSubject = new BehaviorSubject();
behaviorSubject.next('behavior subject');
behaviorSubject.subscribe(x => console.log(x)); // 'behavior subject'
看出來了嗎?BehaviorSubject
可以立即發出最新的值,這對於某些元件訂閱事件是必要的,有時候必須立刻給出最新值從而保證元件資料的時效性,而不是等待下一次使用者操作去觸發。
其餘部分都是大同小異,我們不再詳細展開,本質上 nz-select
元件的核心就是元件拆分和資料同步,拆分元件的目的是便於維護和管理,但是這也會引出資料互動同步的問題,它是通過 Service
來進行資料管理, Rxjs 的 Subject
來傳遞/訂閱,保證了各部分元件的資料流穩定。