Angular 未更新ControlValueAccessor ngModel
这是一个简单的自定义表单控件Angular 未更新ControlValueAccessor ngModel,angular,angular-forms,angular2-ngmodel,controlvalueaccessor,Angular,Angular Forms,Angular2 Ngmodel,Controlvalueaccessor,这是一个简单的自定义表单控件 @Component({ selector: 'app-custom-control', template: ` {{ value }} <input [ngModel]="value" (ngModelChange)="onChange($event)"> `, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() =&g
@Component({
selector: 'app-custom-control',
template: `
{{ value }}
<input [ngModel]="value" (ngModelChange)="onChange($event)">
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomControlComponent),
multi: true,
}]
})
export class CustomControlComponent implements ControlValueAccessor {
private value: any;
private onChange: (val) => void;
private onTouch: () => void;
writeValue(value: any) {
this.value = value;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
}
@组件({
选择器:“应用程序自定义控件”,
模板:`
{{value}}
`,
供应商:[{
提供:NG_值访问器,
useExisting:forwardRef(()=>CustomControlComponent),
多:是的,
}]
})
导出类CustomControlComponent实现ControlValueAccessor{
私人价值:任何;
私有变更:(val)=>无效;
私有onTouch:()=>无效;
writeValue(值:任意){
这个值=值;
}
注册变更(fn:任何):无效{
this.onChange=fn;
}
注册人(fn:任何):无效{
this.onTouch=fn;
}
}
使用方法如下:
@Component({
selector: 'my-app',
template: `
<app-custom-control
[ngModel]="model"
(ngModelChange)="onChange($event)">
</app-custom-control>
<input [ngModel]="model" (ngModelChange)="onChange($event)">
`,
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
model = 'hello';
onChange(value) {
this.model = value;
}
}
@组件({
选择器:“我的应用程序”,
模板:`
编辑:
通过更简单的示例(无内部输入)可以看到实际问题:
@组件({
选择器:“应用程序自定义控件”,
模板:`
{{value}}
设置新值
`,
供应商:[{
提供:NG_值访问器,
useExisting:forwardRef(()=>CustomControlComponent),
多:是的,
}]
})
导出类CustomControlComponent实现ControlValueAccessor{
价值:任何;
onChange:(val)=>无效;
未触及:()=>无效;
writeValue(值:任意){
这个值=值;
}
注册变更(fn:任何):无效{
this.onChange=fn;
}
注册人(fn:任何):无效{
this.ontoched=fn;
}
}
单击自定义控件内的按钮后,将更新父控件上的属性值,但不更新ngModel。例如:要使其正常工作,您必须对驻留在自定义控件.component.ts中的输入使用香蕉在框中的语法
自定义控件.component.ts
这是因为当您在外部输入中键入时,将执行CustomControlComponent
的ControlValueAccessor.writeValue()
,这反过来将更新内部输入
让我们把它分成几个小步骤
1) 输入外部输入
2) 更改检测被触发
3) 最终将到达NgModel
指令中的ngochanges
(该指令绑定到自定义控件
),这将导致FormControl
实例在下一次勾选中更新
@指令({
选择器:'[ngModel]:not([formControlName]):not([formControlName]),
提供程序:[formControlBinding],
exportAs:'ngModel'
})
导出类NgModel扩展了NgControl实现的OnChanges,
探空{
/* ... */
ngOnChanges(更改:SimpleChanges){
这是一个错误;
如果(!this._注册)this._setUpControl();
如果(变更中的“isDisabled”){
此._已更新禁用(更改);
}
如果(iPropertyUpdated(更改,此.viewModel)){
this.\u updateValue(this.model);
this.viewModel=this.model;
}
/* ... */
private\u updateValue(值:any):void{
那么我同意了(
()=>{this.control.setValue(值,{emitViewToModelChange:false});
});
}
}
}
4) FormControl.setValue()
将调用已注册的更改函数回调,该回调将依次调用ControlValueAccessor.writeValue
control.registerChange((newValue:any,emitModelEvent:boolean)=>{
//控件->视图
dir.valueAccessor!.writeValue(newValue);
//控制模式
if(emitModelEvent)dir.viewtomodeUpdate(newValue);
});
其中dir.valueAccessor!.writeValue(newValue)
将是CustomControlComponent.writeValue
函数
writeValue(值:任意){
这个值=值;
}
这就是你的内部输入被外部输入更新的原因
现在,为什么不反过来呢
当您在内部输入中键入时,它将仅调用其onChange
函数,如下所示:
函数setUpViewChangePipeline(控件:FormControl,dir:NgControl):无效{
dir.valueAccessor!.registerChange((newValue:any)=>{
控件。_pendingValue=新值;
控件。_pendingChange=true;
控件。_pendingDirty=true;
if(control.updateOn==='change')updateControl(control,dir);
});
}
这将再次启动updateControl
功能
函数updateControl(控件:FormControl,dir:NgControl):void{
if(control._pendingDirty)control.markAsDirty();
control.setValue(control._pendingValue,{emitModelToViewChange:false});
方向视图更新(控制待定值);
控件。_pendingChange=false;
}
查看updateControl
,您将看到它具有{emitModelToViewChange:false}
标志。查看FormControl.setValue()
,我们将看到该标志阻止内部输入被更新
setValue(值:任意,选项:{
只有你自己?:布尔值,
emitEvent?:布尔值,
emitModelToViewChange?:布尔值,
emitViewToModelChange?:布尔值
}={}):无效{
(这是{value:any})。value=this。_pendingValue=value;
//这里!
if(this.\u onChange.length&&options.emitModelToViewChange!==false){
这是一个很好的例子(
(changeFn)=>changeFn(this.value,options.emitViewToModelChange!==false));
}
此.updateValue和Validity(选项);
}
实际上,只有内部输入没有更新,但是绑定到该输入的FormControl
实例被更新。这可以通过以下操作看到:
custom-control.component.html
{{value}
{i.control.value | json}
您的答案确实正确,谢谢。但实际问题并不是内部输入的ngModel。我编辑了这个问题以澄清问题所在。@БССССююа,我的答案中揭示了发生这种情况的原因。为了解决更新后的问题,您将
@Component({
selector: 'app-custom-control',
template: `
{{ value }}
<button (click)="onChange('new value')">set new value</button>
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomControlComponent),
multi: true,
}]
})
export class CustomControlComponent implements ControlValueAccessor {
value: any;
onChange: (val) => void;
onTouched: () => void;
writeValue(value: any) {
this.value = value;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
}