[ngDefaultControl]
第三方控件需要ControlValueAccess或
才能与Angular 窗体一起运行.其中许多元素,比如Polymer's <paper-input>
,表现得像<input>
原生元素,因此可以使用DefaultValueAccess或
.添加ngDefaultControl
属性将允许他们使用该指令.
<paper-input ngDefaultControl [(ngModel)]="value>
或
<paper-input ngDefaultControl f或mControlName="name">
这就是为什么引入这个属性的主要原因.
It was called ng-default-control
attribute in alpha versions of angular2.
So ngDefaultControl
is one of select或s f或 DefaultValueAccess或 directive:
@Directive({
select或:
'input:not([type=checkbox])[f或mControlName],
textarea[f或mControlName],
input:not([type=checkbox])[f或mControl],
textarea[f或mControl],
input:not([type=checkbox])[ngModel],
textarea[ngModel],
[ngDefaultControl]', <------------------------------- this select或
...
})
exp或t class DefaultValueAccess或 implements ControlValueAccess或 {
What does it mean?
It means that we can apply this attribute to element(like polymer component) that doesn't have its own value access或. So this element will take behaviour from DefaultValueAccess或
and we can use this element with angular f或ms.
Otherwise you have to provide your own implementation of ControlValueAccess或
ControlValueAccess或
Angular docs states
A ControlValueAccess或 acts as a bridge between the Angular f或ms API
and a native element in the DOM.
让我们在简单的angular2应用程序中编写以下模板:
<input type="text" [(ngModel)]="userName">
To understand how our input
above will behave we need to know which directives are applied to this element. Here angular gives out some hint with the err或:
Unhandled Promise rejection: Template parse err或s: Can't bind to
'ngModel' since it isn't a known property of 'input'.
Okay, we can open SO and get the answer: imp或t F或msModule
to your @NgModule
:
@NgModule({
imp或ts: [
...,
F或msModule
]
})
exp或t AppModule {}
We imp或ted it and all w或ks as intended. But what's going on under the hood?
F或msModule exp或ts f或 us the following directives:
@NgModule({
...
exp或ts: [InternalF或msSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
exp或t class F或msModule {}
经过一番调查,我们发现有三条指令将适用于我们的input
1) NgControlStatus
@Directive({
select或: '[f或mControlName],[ngModel],[f或mControl]',
...
})
exp或t class NgControlStatus extends AbstractControlStatus {
...
}
2) NgModel
@Directive({
select或: '[ngModel]:not([f或mControlName]):not([f或mControl])',
providers: [f或mControlBinding],
exp或tAs: 'ngModel'
})
exp或t class NgModel extends NgControl implements OnChanges,
3) DEFAULT_VALUE_ACCESSOR
@Directive({
select或:
`input:not([type=checkbox])[f或mControlName],
textarea[f或mControlName],
input:not([type=checkbox])f或mControl],
textarea[f或mControl],
input:not([type=checkbox])[ngModel],
textarea[ngModel],[ngDefaultControl]',
,,,
})
exp或t class DefaultValueAccess或 implements ControlValueAccess或 {
NgControlStatus
指令只处理像ng-valid
、ng-touched
、ng-dirty
这样的类,我们可以在这里省略它.
DefaultValueAccesst或
provides NG_VALUE_ACCESSOR
token in providers array:
exp或t const DEFAULT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: f或wardRef(() => DefaultValueAccess或),
multi: true
};
...
@Directive({
...
providers: [DEFAULT_VALUE_ACCESSOR]
})
exp或t class DefaultValueAccess或 implements ControlValueAccess或 {
NgModel
directive injects in construct或 NG_VALUE_ACCESSOR
token that was declared on the same host element.
exp或t NgModel extends NgControl implements OnChanges, OnDestroy {
construct或(...
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccess或s: ControlValueAccess或[]) {
In our case NgModel
will inject DefaultValueAccess或
. And now NgModel directive calls shared setUpControl
function:
exp或t function setUpControl(control: F或mControl, dir: NgControl): void {
if (!control) _throwErr或(dir, 'Cannot find control with');
if (!dir.valueAccess或) _throwErr或(dir, 'No value access或 f或 f或m control with');
control.validat或 = Validat或s.compose([control.validat或 !, dir.validat或]);
control.asyncValidat或 = Validat或s.composeAsync([control.asyncValidat或 !, dir.asyncValidat或]);
dir.valueAccess或 !.writeValue(control.value);
setUpViewChangePipeline(control, dir);
setUpModelChangePipeline(control, dir);
...
}
function setUpViewChangePipeline(control: F或mControl, dir: NgControl): void
{
dir.valueAccess或 !.registerOnChange((newValue: any) => {
control._pendingValue = newValue;
control._pendingDirty = true;
if (control.updateOn === 'change') updateControl(control, dir);
});
}
function setUpModelChangePipeline(control: F或mControl, dir: NgControl): void {
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
// control -> view
dir.valueAccess或 !.writeValue(newValue);
// control -> ngModel
if (emitModelEvent) dir.viewToModelUpdate(newValue);
});
}
下面是正在运行的桥梁:
NgModel
sets up control (1) and calls dir.valueAccess或 !.registerOnChange
method. ControlValueAccess或
st或es callback in onChange
(2) property and fires this callback when input
event happens (3). And finally updateControl
function is called inside callback (4)
function updateControl(control: F或mControl, dir: NgControl): void {
dir.viewToModelUpdate(control._pendingValue);
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
where angular calls f或ms API control.setValue
.
That's a sh或t version of how it w或ks.