【薦】深入Angular自定義表單控制項

推薦文章

Maxim Koretskyi : Never again be confused when implementing ControlValueAccessor in Angular forms

推薦理由

在大型複雜的管理後台項目中,很有可能你會遇到需要自定義表單控制項(Custom form control)。很多文章都介紹了此時應該定義ControlValueAccessor,也展示了如何實現,但並沒有說出為什麼,這個類在Angular的表單架構中起了什麼作用。該文章就解決了為什麼的問題,讓你從原理理解自定義表單控制項。

文章概要

首先,只要你創建表單,Angular就會創建對應FormControl,無論是模板驅動表單還是響應式表單。模板驅動表單的FormControl是由NgModel指令隱性創建,而響應式表單是由你自己創建,通過FormControlName指令將Angular表單元素和原生表單元素進行綁定。

// packages/forms/src/directives/ng_model.ts@Directive({ selector: [ngModel]..., ...})export class NgModel ... { _control = new FormControl(); <---------------- 這裡

也就是說在Angular中的表單,不是原生表單,而是封裝後的Angular表單。不僅僅是原生的表單控制項可以處理封裝成Angular表單,其他自定義的Angular組件也可以,只要定義了ControlValueAccessor。

那ControlValueAccessor是什麼呢?它是原生元素和Angular表單之間的橋樑,將組件或者指令繼承ControlValueAccessor的介面就能變成Angular表單使用了。

ControlValueAccessor介面長這樣:

// packages/forms/src/directives/control_value_accessor.tsinterface ControlValueAccessor { writeValue(obj: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void ...}

writeValue是在初始化的時候將formControl的值傳遞給原生表單控制項;registerOnChange用來獲取原生表單控制項的值更新時通知Angular表單控制項更新的函數;registerOnTouched用來獲取通知用戶正在交互的函數。

明確來說,那些原生表單控制項都有其對應的ControlValueAccessor:

+------------------------------------+----------------------+| Accessor | Form Element |+------------------------------------+----------------------+| DefaultValueAccessor | input, textarea || CheckboxControlValueAccessor | input[type=checkbox] || NumberValueAccessor | input[type=number] || RadioControlValueAccessor | input[type=radio] || RangeValueAccessor | input[type=range] || SelectControlValueAccessor | select || SelectMultipleControlValueAccessor | select[multiple] |

那原生表單控制項和Angular表單控制項保持一致的原理是什麼呢?

我們看下formControl指令的實現:

// packages/forms/src/directives/reactive_directives/form_control_directive.tsexport class FormControlDirective ... { ... ngOnChanges(changes: SimpleChanges): void { if (this._isControlChanged(changes)) { setUpControl(this.form, this);

formControl指令調用了setUpControl函數來實現formControl和ControlValueAccessor之間的交互。

// packages/forms/src/directives/shared.tsexport function setUpControl(control: FormControl, dir: NgControl) { // 初始化原生表單控制項 dir.valueAccessor.writeValue(control.value); // 監聽原生表單控制項,將值同步給form control dir.valueAccessor.registerOnChange((newValue: any) => { ... control.setValue(newValue, {emitModelToViewChange: false}); }); // 反之,監聽form control,將值同步給原生表單控制項 control.registerOnChange((newValue: any, ...) => { dir.valueAccessor.writeValue(newValue); });

到此,我們應該明白ControlValueAccessor中定義writeValue等函數是怎麼work了吧。

以上就是最重要的原理部分。

接下來,作者通過第三方組件jquery-slider來演示了如何用Angular封裝第三方組件庫,以及如何將該組件封裝成自定義表單控制項。具體教程可以看原文。

延伸閱讀

  • semlinker: Angular 4.x 自定義表單控制項

本文首發知乎野草。如有不當之處,歡迎指正。

推薦閱讀:

【讀書筆記】鋒利的 jQuery
0基礎,如何學H5開發?
ReactEurope 2016 小記 - 上
React新引擎React Fiber究竟要解決什麼問題?
騷年,我看你骨骼驚奇,來跟我學前端吧。

TAG:Angular? | 前端组件 | 前端框架 |