[ngDefaultControl]
第三方控件要求a ControlValueAccessor
以角形式起作用。它们中的许多元素(例如Polymer的元素)的<paper-input>
行为都类似于本<input>
机元素,因此可以使用DefaultValueAccessor
。添加ngDefaultControl
属性将允许他们使用该指令。
<paper-input ngDefaultControl [(ngModel)]="value>
要么
<paper-input ngDefaultControl formControlName="name">
因此,这就是引入这种属性的主要原因。
在angular2的alpha版本中称为ng-default-control
属性。
因此ngDefaultControl
,DefaultValueAccessor指令的选择器之一是:
@Directive({
selector:
'input:not([type=checkbox])[formControlName],
textarea[formControlName],
input:not([type=checkbox])[formControl],
textarea[formControl],
input:not([type=checkbox])[ngModel],
textarea[ngModel],
[ngDefaultControl]', <------------------------------- this selector
...
})
export class DefaultValueAccessor implements ControlValueAccessor {
这是什么意思?
这意味着我们可以将此属性应用于没有自己的值访问器的元素(如聚合物组件)。因此,此元素将采取行为DefaultValueAccessor
,我们可以将此元素与角度形式一起使用。
否则,您必须提供自己的实现 ControlValueAccessor
ControlValueAccessor
角度文档状态
ControlValueAccessor充当Angular表单API和DOM中的本机元素之间的桥梁。
让我们在简单的angular2应用程序中编写以下模板:
<input type="text" [(ngModel)]="userName">
要了解我们input
上面的行为,我们需要知道将哪些指令应用于此元素。在这里,角度会给出一些错误提示:
未处理的承诺拒绝:模板解析错误:由于它不是'input'的已知属性,因此无法绑定到'ngModel'。
好的,我们可以打开SO并获取答案:导入FormsModule
到您的@NgModule
:
@NgModule({
imports: [
...,
FormsModule
]
})
export AppModule {}
我们将其导入,所有作品均按预期进行。但是幕后到底是怎么回事?
FormsModule为我们导出以下指令:
@NgModule({
...
exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}
经过一番调查,我们发现三个指令将应用于我们 input
1)NgControlStatus
@Directive({
selector: '[formControlName],[ngModel],[formControl]',
...
})
export class NgControlStatus extends AbstractControlStatus {
...
}
2)NgModel
@Directive({
selector: '[ngModel]:not([formControlName]):not([formControl])',
providers: [formControlBinding],
exportAs: 'ngModel'
})
export class NgModel extends NgControl implements OnChanges,
3)DEFAULT_VALUE_ACCESSOR
@Directive({
selector:
`input:not([type=checkbox])[formControlName],
textarea[formControlName],
input:not([type=checkbox])formControl],
textarea[formControl],
input:not([type=checkbox])[ngModel],
textarea[ngModel],[ngDefaultControl]',
,,,
})
export class DefaultValueAccessor implements ControlValueAccessor {
NgControlStatus
指令只是操纵类一样ng-valid
,ng-touched
,ng-dirty
我们可以在这里省略。
DefaultValueAccesstor
NG_VALUE_ACCESSOR
在提供者数组中提供令牌:
export const DEFAULT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DefaultValueAccessor),
multi: true
};
...
@Directive({
...
providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {
NgModel
指令将在NG_VALUE_ACCESSOR
同一主机元素上声明的构造函数令牌注入。
export NgModel extends NgControl implements OnChanges, OnDestroy {
constructor(...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
在我们的情况下NgModel
将注入DefaultValueAccessor
。现在,NgModel指令调用共享setUpControl
函数:
export function setUpControl(control: FormControl, dir: NgControl): void {
if (!control) _throwError(dir, 'Cannot find control with');
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]);
dir.valueAccessor !.writeValue(control.value);
setUpViewChangePipeline(control, dir);
setUpModelChangePipeline(control, dir);
...
}
function setUpViewChangePipeline(control: FormControl, dir: NgControl): void
{
dir.valueAccessor !.registerOnChange((newValue: any) => {
control._pendingValue = newValue;
control._pendingDirty = true;
if (control.updateOn === 'change') updateControl(control, dir);
});
}
function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
// control -> view
dir.valueAccessor !.writeValue(newValue);
// control -> ngModel
if (emitModelEvent) dir.viewToModelUpdate(newValue);
});
}
这是行动的桥梁:
NgModel
设置控件(1)并调用dir.valueAccessor !.registerOnChange
方法。ControlValueAccessor
将回调存储在onChange
(2)属性中,并在input
事件(3)发生时触发此回调。最后updateControl
在回调函数内部调用函数(4)
function updateControl(control: FormControl, dir: NgControl): void {
dir.viewToModelUpdate(control._pendingValue);
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
角度调用形成API的地方control.setValue
。
这是其工作方式的简短版本。
@Input() ngModel
和@Output() ngModelChange
双向结合,我想这应该是足够的桥梁。这看起来像以完全不同的方式执行相同的操作。也许我不应该为自己的领域命名ngModel
?