这是简单的自定义窗体控件。
@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;
}
}如下所用:
@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;
}
}我不理解的是为什么控件的ngModel只是根据外部输入的值进行更新,而不是在使用内部输入的情况下更新呢?这里的实例:https://stackblitz.com/edit/angular-7apjhg
编辑:
简单的例子(没有内部输入)可以看出实际问题:
@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;
}
}单击自定义控件内的按钮后,将更新父控件上的属性值,但ngModel不会更新。更新示例:https://stackblitz.com/edit/angular-tss2f3
发布于 2020-01-13 13:48:26
为了使其工作,您必须对驻留在中的输入使用语法中的custom-control.component.ts香蕉
custom-control.component.ts
<input [(ngModel)]="value" (ngModelChange)="onChange($event)">工作实例。
这是因为当您在外部输入输入时,将执行CustomControlComponent的ControlValueAccessor.writeValue(),这反过来将更新内部输入。
让我们把它分成更小的步骤。
1)输入外部输入
2)触发更改检测。
3) ngOnChanges从NgModel指令(绑定到custom-control)将最终到达,这将导致在下一个滴答中更新FormControl实例。
@Directive({
selector: '[ngModel]:not([formControlName]):not([formControl])',
providers: [formControlBinding],
exportAs: 'ngModel'
})
export class NgModel extends NgControl implements OnChanges,
OnDestroy {
/* ... */
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;
}
/* ... */
private _updateValue(value: any): void {
resolvedPromise.then(
() => { this.control.setValue(value, { emitViewToModelChange: false });
});
}
}
}4) FormControl.setValue()将调用已注册的更改函数回调,该回调将反过来调用ControlValueAccessor.writeValue
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
// control -> view
dir.valueAccessor !.writeValue(newValue);
// control -> ngModel
if (emitModelEvent) dir.viewToModelUpdate(newValue);
});其中dir.valueAccessor !.writeValue(newValue)将是CustomControlComponent.writeValue函数。
writeValue(value: any) {
this.value = value;
}这就是为什么你的内部输入是由外部输入更新的。
现在,为什么它不反过来工作呢?
当您在内部输入输入时,它将只调用的及其onChange函数,如下所示:
function setUpViewChangePipeline(control: FormControl, dir: NgControl): void {
dir.valueAccessor !.registerOnChange((newValue: any) => {
control._pendingValue = newValue;
control._pendingChange = true;
control._pendingDirty = true;
if (control.updateOn === 'change') updateControl(control, dir);
});
}这将再次成为updateControl函数。
function updateControl(control: FormControl, dir: NgControl): void {
if (control._pendingDirty) control.markAsDirty();
control.setValue(control._pendingValue, {emitModelToViewChange: false});
dir.viewToModelUpdate(control._pendingValue);
control._pendingChange = false;
}查看updateControl内部,您将看到它有{ emitModelToViewChange: false }标志。查看FormControl.setValue(),我们将看到标志阻止内部输入被更新。
setValue(value: any, options: {
onlySelf?: boolean,
emitEvent?: boolean,
emitModelToViewChange?: boolean,
emitViewToModelChange?: boolean
} = {}): void {
(this as{value: any}).value = this._pendingValue = value;
// Here!
if (this._onChange.length && options.emitModelToViewChange !== false) {
this._onChange.forEach(
(changeFn) => changeFn(this.value, options.emitViewToModelChange !== false));
}
this.updateValueAndValidity(options);
}实际上,只有内部输入没有更新,但是绑定到该输入的FormControl实例将被更新。通过这样做可以看出这一点:
custom-control.component.html
{{ value }}
<input #i="ngModel" [ngModel]="value" (ngModelChange)="onChange($event)">
{{ i.control.value | json }} <!-- Always Updated -->https://stackoverflow.com/questions/59717253
复制相似问题