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;
}
}