檢視新版教程,請訪問前端修仙之路
在 Angular 4.x 中對於使用 Template-Driven 表單場景,如果需要實現表單資料繫結。我們就需要引入 ngModel
指令。該指令用於基於 domain 模型,建立 FormControl
例項,並將建立的例項繫結到表單控制元件元素上。
ngModel 使用示例
ngModel
app.component.ts
@Component({
selector: 'exe-app',
template: `
<form novalidate #f="ngForm">
Name: <input type="text" name="username" ngModel>
</form>
{{ f.value | json }}
`,
})
export class AppComponent implements OnInit { }
複製程式碼
在 <form>
表單中使用 ngModel
時,我們需要設定一個 name
屬性,以便該控制元件可以使用該名稱在表單中進行註冊。
單向繫結 - [ngModel]
app.component.ts
@Component({
selector: 'exe-app',
template: `
<form novalidate #f="ngForm">
Name: <input type="text" name="username" [ngModel]="user.username">
</form>
{{ user | json }}
`,
})
export class AppComponent implements OnInit {
user: { username: string };
ngOnInit() {
this.user = { username: 'Semlinker' };
}
}
複製程式碼
雙向繫結 - [(ngModel)]
表單中應用
app.component.ts
@Component({
selector: 'exe-app',
template: `
<form novalidate #f="ngForm">
Name: <input type="text" name="username" [(ngModel)]="user.username">
</form>
{{ user | json }}
`,
})
export class AppComponent implements OnInit {
user: { username: string };
ngOnInit() {
this.user = { username: 'Semlinker' };
}
}
複製程式碼
單獨應用
import { Component } from '@angular/core';
@Component({
selector: 'exe-app',
template: `
<input name="username" [(ngModel)]="username">
{{username}}
`,
})
export class AppComponent {
username: string;
}
複製程式碼
ngModelOptions - [ngModelOptions]
當你在使用 ngModel 時未設定 name 屬性,如下所示:
<form novalidate #f="ngForm">
Name: <input type="text" [(ngModel)]="user.username">
</form>
複製程式碼
當你執行時,瀏覽器控制檯將會丟擲以下異常資訊:
Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
複製程式碼
以上異常資訊告訴我們,如果在表單標籤中使用 ngModel,則必須設定 name 屬性,或者在 ngModelOptions 中必須將表單控制元件定義為 "standalone"。依據上述異常資訊,我們做如下調整:
<form novalidate #f="ngForm">
Name: <input type="text" [(ngModel)]="user.username"
[ngModelOptions]="{standalone: true}">
</form>
複製程式碼
接下來我們看一下 ngModelOptions 支援的物件型別:
@Input('ngModelOptions') options: {name?: string, standalone?: boolean};
複製程式碼
禁用控制元件 - disabled
<form novalidate #f="ngForm">
Name: <input type="text" name="username"
[(ngModel)]="user.username" disabled="true">
</form>
複製程式碼
監聽 ngModelChange 事件 - (ngModelChange)
app.component.ts
@Component({
selector: 'exe-app',
template: `
<form novalidate #f="ngForm">
Name: <input type="text" name="username" (ngModelChange)="userNameChange($event)"
[(ngModel)]="user.username">
</form>
{{ user | json }}
`,
})
export class AppComponent implements OnInit {
user: { username: string };
ngOnInit() {
this.user = { username: 'Semlinker' };
}
userNameChange(name: string) {
console.log(name);
}
}
複製程式碼
獲取關聯的 NgModel 物件
app.component.ts
@Component({
selector: 'exe-app',
template: `
<form novalidate #f="ngForm">
Name: <input type="text" name="username" #userName="ngModel"
[(ngModel)]="user.username">
</form>
{{ userName.control | json }}
`,
})
export class AppComponent implements OnInit {
user: { username: string };
ngOnInit() {
this.user = { username: 'Semlinker' };
}
}
複製程式碼
通過使用 userName="ngModel"
方式,我們可以獲取表單控制元件關聯的 NgModel 物件,進而獲取控制元件當前控制元件的相關資訊,如控制元件的當前的狀態或控制元件驗證資訊等。
完整示例
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'exe-app',
template: `
<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
<input name="first" ngModel required #first="ngModel">
<input name="last" ngModel>
<button>Submit</button>
</form>
<p>First name value: {{ first.value }}</p>
<p>First name valid: {{ first.valid }}</p>
<p>Form value: {{ f.value | json }}</p>
<p>Form valid: {{ f.valid }}</p>
`,
})
export class AppComponent {
onSubmit(f: NgForm) {
console.log(f.value); // { first: '', last: '' }
console.log(f.valid); // false
}
}
複製程式碼
ngModel 指令詳解
ngModel 指令定義
@Directive({
selector: '[ngModel]:not([formControlName]):not([formControl])',
providers: [formControlBinding],
exportAs: 'ngModel'
})
複製程式碼
formControlBinding 定義
export const formControlBinding: any = {
provide: NgControl,
useExisting: forwardRef(() => NgModel)
};
複製程式碼
相關說明
- selector 中
[ngModel]:not([formControlName]):not([formControl])
表示該指令只應用於 Template-Driven 表單中。 - exportAs - 表示可以使用
first="ngModel"
語法獲取 NgModel 物件
ngModel 指令輸入與輸出屬性
輸入屬性
@Input() name: string;
@Input('disabled') isDisabled: boolean;
@Input('ngModel') model: any;
@Input('ngModelOptions') options: {name?: string, standalone?: boolean};
複製程式碼
輸出屬性
@Output('ngModelChange') update = new EventEmitter();
複製程式碼
NgModel 類
// angular2/packages/forms/src/directives/ng_model.ts
export class NgModel extends NgControl implements OnChanges,
OnDestroy {
/** @internal */
_control = new FormControl(); // 建立FormControl物件
/** @internal */
_registered = false; // 用於標識控制元件是否已註冊
viewModel: any; // 用於儲存前一次model的值
...
}
複製程式碼
NgModel 建構函式
constructor(
@Optional() @Host() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[]) {
super();
this._parent = parent;
this._rawValidators = validators || [];
this._rawAsyncValidators = asyncValidators || [];
this.valueAccessor = selectValueAccessor(this, valueAccessors);
}
複製程式碼
相關說明
- @Optional() - 表示該依賴物件是可選的
- @Host() - 表示從宿主元素注入器獲取依賴物件
- @Self() - 表示從當前注入器獲取依賴物件
- @Inject() - 用於注入 Token (new InjectionToken) 對應的非 Type 型別依賴物件
- 建構函式執行的操作:
- 獲取 ControlContainer (控制元件容器)物件
- 獲取控制元件上的同步驗證器
- 獲取控制元件上的非同步驗證器
- 獲取控制元件上的 ControlValueAccessor
NgModel 生命週期鉤子
ngOnChanges
ngOnChanges(changes: SimpleChanges) {
this._checkForErrors();
if (!this._registered) this._setUpControl();
if ('isDisabled' in changes) {
this._updateDisabled(changes);
}
if (isPropertyUpdated(changes, this.viewModel)) {
this._updateValue(this.model);
this.viewModel = this.model;
}
}
複製程式碼
_checkForErrors()
private _checkForErrors(): void {
if (!this._isStandalone()) {
this._checkParentType();
}
this._checkName();
}
// 判斷是否設定standalone屬性
private _isStandalone(): boolean {
return !this._parent || (this.options && this.options.standalone);
}
/**
* 1.ngModel指令不能與formGroupName或formArrayName指令一起使用,需改用
* formControlName或調整ngModel的父控制元件使用的指令為ngModelGroup。
*
* 2.ngModel不能被註冊到使用formGroup指令的表單中,需改用formControlName或設定
* ngModelOptions物件中的standalone屬性,避免註冊該控制元件。
*/
private _checkParentType(): void {
if (!(this._parent instanceof NgModelGroup) &&
this._parent instanceof AbstractFormGroupDirective) {
TemplateDrivenErrors.formGroupNameException();
} else if (!(this._parent instanceof NgModelGroup) &&
!(this._parent instanceof NgForm)) {
TemplateDrivenErrors.modelParentException();
}
}
/**
* 驗證是否設定name屬性
*
* 如果在表單標籤中使用 ngModel,則必須設定 name 屬性,或者在ngModelOptions中必須將
* 表單控制元件定義為"standalone"。
*
* <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone:
* true}">
*/
private _checkName(): void {
if (this.options && this.options.name) this.name = this.options.name;
if (!this._isStandalone() && !this.name) {
TemplateDrivenErrors.missingNameException();
}
}
複製程式碼
_setUpControl()
// 初始化控制元件
private _setUpControl(): void {
this._isStandalone() ? this._setUpStandalone() :
// 在ControlContainer所屬的form中註冊該控制元件
this.formDirective.addControl(this);
this._registered = true; // 標識已註冊
}
// 若設定standalone屬性,則初始化該控制元件,並更新控制元件的值和驗證狀態
private _setUpStandalone(): void {
setUpControl(this._control, this);
this._control.updateValueAndValidity({emitEvent: false});
}
// 獲取ControlContainer所屬的form
get formDirective(): any {
return this._parent ? this._parent.formDirective : null;
}
複製程式碼
_updateDisabled()
若設定 isDisabled
輸入屬性,則更新控制元件的 disabled 屬性:
// 更新控制元件的disabled狀態
private _updateDisabled(changes: SimpleChanges) {
// 獲取disabled輸入屬性的當前值
const disabledValue = changes['isDisabled'].currentValue;
// 判斷是否設定為disabled
const isDisabled = disabledValue === '' ||
(disabledValue && disabledValue !== 'false');
resolvedPromise.then(() => {
if (isDisabled && !this.control.disabled) {
this.control.disable(); // 禁用控制元件
} else if (!isDisabled && this.control.disabled) {
this.control.enable(); // 啟用控制元件
}
});
}
複製程式碼
isPropertyUpdated()
// 判斷屬性是否更新
export function isPropertyUpdated(changes: {[key: string]: any},
viewModel: any): boolean {
if (!changes.hasOwnProperty('model')) return false; // @Input('ngModel') model: any;
const change = changes['model'];
if (change.isFirstChange()) return true; // 判斷是否首次改變
return !looseIdentical(viewModel, change.currentValue);
}
// JS has NaN !== NaN
export function looseIdentical(a: any, b: any): boolean {
return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a)
&& isNaN(b);
}
複製程式碼
_updateValue()
// 更新控制元件的值
private _updateValue(value: any): void {
resolvedPromise.then(
() => { this.control.setValue(value, {emitViewToModelChange: false});
});
}
const resolvedPromise = Promise.resolve(null);
複製程式碼
ngOnDestroy()
// 指令銷燬時,從formDirective中移除該控制元件
ngOnDestroy(): void {
this.formDirective && this.formDirective.removeControl(this);
}
複製程式碼
NgModel 方法
get control(): FormControl
// 獲取控制元件
get control(): FormControl { return this._control; }
/** @internal */
_control = new FormControl();
複製程式碼
get path(): string[]
// 獲取控制元件的訪問路徑
get path(): string[] {
return this._parent ? controlPath(this.name, this._parent) : [this.name];
}
複製程式碼
get validator(): ValidatorFn
// 獲取同步驗證器
get validator(): ValidatorFn {
return composeValidators(this._rawValidators);
}
export interface ValidatorFn { (c: AbstractControl): ValidationErrors|null; }
複製程式碼
get asyncValidator(): AsyncValidatorFn
// 獲取非同步驗證器
get asyncValidator(): AsyncValidatorFn {
return composeAsyncValidators(this._rawAsyncValidators);
}
export interface AsyncValidatorFn {
(c: AbstractControl): Promise<ValidationErrors|null>|Observable<ValidationErrors|null>;
}
複製程式碼
viewToModelUpdate(newValue: any): void
// 觸發ngModelChange事件
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
// @Output('ngModelChange') update = new EventEmitter();
this.update.emit(newValue);
}
複製程式碼
NgControl 抽象類
// angular2/packages/forms/src/directives/ng_control.ts
// 所有控制元件指令都需繼承的基類,繫結FormControl物件至DOM元素
export abstract class NgControl extends AbstractControlDirective {
/** @internal */
_parent: ControlContainer = null;
name: string = null;
valueAccessor: ControlValueAccessor = null;
/** @internal */
_rawValidators: Array<Validator|ValidatorFn> = [];
/** @internal */
_rawAsyncValidators: Array<AsyncValidator|AsyncValidatorFn> = [];
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
abstract viewToModelUpdate(newValue: any): void;
}
複製程式碼
AbstractControlDirective 抽象類
// angular2/packages/forms/src/directives/abstract_control_directive.ts
export abstract class AbstractControlDirective {
// 獲取控制元件
get control(): AbstractControl { throw new Error('unimplemented'); }
// 獲取控制元件的值
get value(): any { return this.control ? this.control.value : null; }
// 控制元件控制元件的驗證狀態 - valid、invalid、pending
get valid(): boolean { return this.control ? this.control.valid : null; }
get invalid(): boolean { return this.control ? this.control.invalid : null; }
get pending(): boolean { return this.control ? this.control.pending : null; }
get pristine(): boolean { return this.control ? this.control.pristine : null; }
get dirty(): boolean { return this.control ? this.control.dirty : null; }
get touched(): boolean { return this.control ? this.control.touched : null; }
get untouched(): boolean { return this.control ? this.control.untouched : null; }
get disabled(): boolean { return this.control ? this.control.disabled : null; }
get enabled(): boolean { return this.control ? this.control.enabled : null; }
// 獲取控制元件驗證異常物件
get errors(): ValidationErrors|null {
return this.control ? this.control.errors : null;
}
// 獲取statusChanges物件
get statusChanges(): Observable<any> {
return this.control ? this.control.statusChanges : null;
}
// 獲取valueChanges物件
get valueChanges(): Observable<any> {
return this.control ? this.control.valueChanges : null;
}
// 獲取控制元件路徑
get path(): string[] { return null; }
// 重設控制元件的值
reset(value: any = undefined): void {
if (this.control) this.control.reset(value);
}
// 判斷是否path路徑對應的控制元件,是否存在errorCode對應的錯誤
hasError(errorCode: string, path: string[] = null): boolean {
return this.control ? this.control.hasError(errorCode, path) : false;
}
// 獲取path路徑對應的控制元件,引數errorCode對應的錯誤
getError(errorCode: string, path: string[] = null): any {
return this.control ? this.control.getError(errorCode, path) : null;
}
}
複製程式碼
input 指令
input 指令定義
@Directive({
selector:`
input:not([type=checkbox])[formControlName],textarea[formControlName],
input:not([type=checkbox])[formControl],textarea[formControl],
input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]
`,
host: {
'(input)': '_handleInput($event.target.value)',
'(blur)': 'onTouched()',
'(compositionstart)': '_compositionStart()',
'(compositionend)': '_compositionEnd($event.target.value)'
},
providers: [DEFAULT_VALUE_ACCESSOR]
})
複製程式碼
相關說明
- compositionstart - 事件觸發於一段文字的輸入之前 (類似於
keydown
事件,但是該事件僅在若干可見字元的輸入之前,而這些可見字元的輸入可能需要一連串的鍵盤操作、語音識別或者點選輸入法的備選詞)。 - compositionend - 事件觸發於完成文字段落輸入或取消輸入
compositionstart、compositionend 的實際應用,請參考 - 應對中文輸入法的字串截斷方案
DEFAULT_VALUE_ACCESSOR
export const DEFAULT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DefaultValueAccessor),
multi: true
};
複製程式碼
DefaultValueAccessor
export class DefaultValueAccessor implements ControlValueAccessor {
onChange = (_: any) => {};
onTouched = () => {};
/** Whether the user is creating a composition string (IME events). */
private _composing = false;
constructor(
private _renderer: Renderer, // 注入Renderer物件
private _elementRef: ElementRef,
@Optional() @Inject(COMPOSITION_BUFFER_MODE)
private _compositionMode: boolean) {
if (this._compositionMode == null) {
this._compositionMode = !_isAndroid();
}
}
// 將模型中的新值寫入檢視或DOM元素屬性中
writeValue(value: any): void {
const normalizedValue = value == null ? '' : value;
this._renderer.setElementProperty(this._elementRef.nativeElement,
'value', normalizedValue);
}
// 設定當控制元件接收到change事件後,呼叫的函式
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
// 設定當控制元件接收到touched事件後,呼叫的函式
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
// 設定控制元件的Disabled狀態
setDisabledState(isDisabled: boolean): void {
this._renderer.setElementProperty(this._elementRef.nativeElement,
'disabled', isDisabled);
}
// 處理input事件
_handleInput(value: any): void {
if (!this._compositionMode || (this._compositionMode && !this._composing)) {
this.onChange(value);
}
}
// 處理compositionstart事件
_compositionStart(): void { this._composing = true; }
// 處理compositionend事件
_compositionEnd(value: any): void {
this._composing = false;
this._compositionMode && this.onChange(value);
}
}
export const COMPOSITION_BUFFER_MODE = new InjectionToken<boolean>
('CompositionEventMode');
// 用於判斷是否處於安卓平臺,composition事件在iOS和Android存在相容性
function _isAndroid(): boolean {
const userAgent = getDOM() ? getDOM().getUserAgent() : '';
return /android (\d+)/.test(userAgent.toLowerCase());
}
複製程式碼
相關說明
為了能夠支援跨平臺,Angular 通過抽象層封裝了不同平臺的差異,統一了 API 介面。如定義了抽象類 Renderer 、抽象類 RootRenderer 等。此外還定義了以下引用型別:ElementRef、TemplateRef、ViewRef 、ComponentRef 和 ViewContainerRef 等。
瞭解詳細的資訊,請檢視 - Angular 2 ElementRef
另外看完上面的程式碼,不知道讀者有沒有以下的疑問:
- writeValue() 方法什麼時候呼叫?
- registerOnChange() 什麼時候呼叫?
- registerOnTouched() 什麼時候呼叫?
為了解開這些疑惑我們就需要分析一下,一個很重要的方法 - setUpControl()。我們先來看一下 setUpControl() 的呼叫的時機點:
NgModel ngOnChanges 生命週期鉤子
ngOnChanges(changes: SimpleChanges) {
...
if (!this._registered) this._setUpControl();
...
}
複製程式碼
_setUpControl() 方法
private _setUpControl(): void {
this._isStandalone() ? this._setUpStandalone() :
// 在ControlContainer所屬的form中註冊該控制元件
this.formDirective.addControl(this);
this._registered = true; // 標識已註冊
}
複製程式碼
_setUpControl() 方法內部,先判斷控制元件有設定 standalone 屬性,如果有的話,則呼叫 _setUpStandalone() 方法:
// 若設定standalone屬性,則初始化該控制元件,並更新控制元件的值和驗證狀態
private _setUpStandalone(): void {
setUpControl(this._control, this); // 呼叫時機點一
this._control.updateValueAndValidity({emitEvent: false});
}
複製程式碼
如果沒有設定 standalone 屬性,則呼叫 this.formDirective.addControl(this)
,這個方法存在於我們的 form
指令中,我們直接看一下具體實現:
addControl(dir: NgModel): void {
resolvedPromise.then(() => {
const container = this._findContainer(dir.path);
dir._control = <FormControl>container.registerControl(dir.name, dir.control);
setUpControl(dir.control, dir); // 呼叫時機點二
dir.control.updateValueAndValidity({emitEvent: false});
});
}
複製程式碼
搞清楚 setUpControl() 呼叫的時機點,是時候分析一下 setUpControl() 方法的具體實現了。
setUpControl()
// angular2/packages/forms/src/directives/shared.ts
export function setUpControl(control: FormControl, dir: NgControl): void {
if (!control) _throwError(dir, 'Cannot find control with');
/**
* NgModel建構函式
* @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]
* this.valueAccessor = selectValueAccessor(this, valueAccessors);
*/
// 判斷控制元件是否實現ControlValueAccessor介面
if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');
// 組合同步驗證器
control.validator = Validators.compose([control.validator, dir.validator]);
// 組合非同步驗證器
control.asyncValidator = Validators.composeAsync([control.asyncValidator,
dir.asyncValidator]);
// 該方法用於將模型中的新值寫入檢視或 DOM 屬性中
dir.valueAccessor.writeValue(control.value);
// view -> model
/**
* @Directive({
* selector: 'input:not([type=checkbox])[formControlName],...',
* host: {
* '(input)': '_handleInput($event.target.value)'
* },
* providers: [DEFAULT_VALUE_ACCESSOR]
* })
* export class DefaultValueAccessor implements ControlValueAccessor {
* // 下面就是呼叫該方法
* registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
*
* // input事件觸發後,呼叫該方法
* _handleInput(value: any): void {
* if (!this._compositionMode || (this._compositionMode && !this._composing)) {
* this.onChange(value); //呼叫下面註冊的onChange函式
* }
* }
* }
*
*/
dir.valueAccessor.registerOnChange((newValue: any) => {
/**
* ngModel指令 - viewToModelUpdate() 方法
*
* viewToModelUpdate(newValue: any): void {
* this.viewModel = newValue; // 更新viewModel
* // @Output('ngModelChange') update = new EventEmitter();
* this.update.emit(newValue); // 觸發ngModelChange事件
* }
*/
dir.viewToModelUpdate(newValue);
control.markAsDirty();
/*
* setValue(value: any, {onlySelf, emitEvent, emitModelToViewChange,
* emitViewToModelChange}: {
* onlySelf?: boolean,
* emitEvent?: boolean,
* emitModelToViewChange?: boolean,
* emitViewToModelChange?: boolean
* } = {}): void {
* this._value = value;
* if (this._onChange.length && emitModelToViewChange !== false) {
* this._onChange.forEach((changeFn) => changeFn(this._value,
* emitViewToModelChange !== false));
* }
* this.updateValueAndValidity({onlySelf, emitEvent});
* }
*/
control.setValue(newValue, {emitModelToViewChange: false}); // 更新控制元件的值
});
// touched
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
/**
* control = new FormControl();
*
* control - _onChange 屬性
* _onChange: Function[] = [];
*
* control - registerOnChange() 方法
* registerOnChange(fn: Function): void { this._onChange.push(fn); }
*/
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
// control -> view
/*
* writeValue(value: any): void {
* const normalizedValue = value == null ? '' : value;
* this._renderer.setElementProperty(this._elementRef.nativeElement, 'value',
* normalizedValue);
* }
*/
dir.valueAccessor.writeValue(newValue);
// control -> ngModel
/**
* ngModel指令 - viewToModelUpdate() 方法
*
* viewToModelUpdate(newValue: any): void {
* this.viewModel = newValue; // 更新viewModel
* // @Output('ngModelChange') update = new EventEmitter();
* this.update.emit(newValue); // 觸發ngModelChange事件
* }
*/
if (emitModelEvent) dir.viewToModelUpdate(newValue);
});
// 當控制元件狀態變成 DISABLED 或從 DISABLED 狀態變化成 ENABLE 狀態時,會呼叫該函式。該函式會根據引數
// 值,啟用或禁用指定的 DOM 元素
if (dir.valueAccessor.setDisabledState) {
control.registerOnDisabledChange(
(isDisabled: boolean) => { dir.valueAccessor.setDisabledState(isDisabled); });
}
// re-run validation when validator binding changes, e.g. minlength=3 -> minlength=4
dir._rawValidators.forEach((validator: Validator | ValidatorFn) => {
if ((<Validator>validator).registerOnValidatorChange)
(<Validator>validator).registerOnValidatorChange(() =>
control.updateValueAndValidity());
});
dir._rawAsyncValidators.forEach((validator: AsyncValidator | AsyncValidatorFn) => {
if ((<Validator>validator).registerOnValidatorChange)
(<Validator>validator).registerOnValidatorChange(() =>
control.updateValueAndValidity());
});
}
複製程式碼
最後我們再看一下 ControlValueAccessor 介面:
ControlValueAccessor
// angular2/packages/forms/src/directives/control_value_accessor.ts
export interface ControlValueAccessor {
writeValue(obj: any): void;
registerOnChange(fn: any): void;
registerOnTouched(fn: any): void;
setDisabledState?(isDisabled: boolean): void;
}
複製程式碼
- writeValue(obj: any):該方法用於將模型中的新值寫入檢視或 DOM 屬性中
- registerOnChange(fn: any):設定當控制元件接收到 change 事件後,呼叫的函式
- registerOnTouched(fn: any):設定當控制元件接收到 touched 事件後,呼叫的函式
- setDisabledState?(isDisabled: boolean):當控制元件狀態變成
DISABLED
或從DISABLED
狀態變化成ENABLE
狀態時,會呼叫該函式。該函式會根據引數值,啟用或禁用指定的 DOM 元素
瞭解 ControlValueAccessor 的詳細資訊,可以參考 - Understanding ControlValueAccessor