angular - 更新 NgModel 时,Angular ngModelChange 迟到了
问题描述
我正在使用 angular 8 制作指令来进行一些处理并将文本转换为大写。简化代码如下:
html:
<input class="form-control" id="label" name="label" required myDirective>
指示:
import { Directive, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';
@Directive({
selector: '[myDirective]'
})
export class Mydirective {
constructor(private control: NgControl) { }
processInput(value: any) {
// do some formatting
return value.toUpperCase();
}
@HostListener('ngModelChange', ['$event'])
ngModelChange(value: any) {
this.control.valueAccessor.writeValue(this.processInput(value));
}
}
现在,视图已正确更新,但模型晚了一步。例如:如果输入文本显示“AAAA”,那么ng-reflect-model
将显示“AAAa”。
我已经在 stackblitz 中重现了错误:Stackblitz 中重现的错误
知道我哪里错了吗?
之前谢谢!
解决方案
TLDR
我的指令.directive.ts
/* ... */
ngOnInit () {
const initialOnChange = (this.ngControl.valueAccessor as any).onChange;
(this.ngControl.valueAccessor as any).onChange = (value) => initialOnChange(this.processInput(value));
}
/* ... */
@HostListener('ngModelChange', ['$event'])
ngModelChange(value: any) {
this.ngControl.valueAccessor.writeValue(this.processInput(value));
}
详细解答
让我们看看为什么它最初不起作用。
Angular 具有某些元素的默认值访问器,例如 forinput type='text'
等input type='checkbox'
...
AControlValueAccessor
是VIEW 层和 MODEL 层之间的中间人。当用户输入输入时,VIEW 通知ControlValueAccessor
,它负责通知 MODEL。
例如,当input
事件发生时,将调用的onChange
方法。以下是每个的样子:ControlValueAccessor
onChange
ControlValueAccessor
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;
}
dir.viewToModelUpdate(control._pendingValue);
是调用ngModelChange
自定义指令中的事件的原因。这意味着模型值是来自输入的值(小写)。并且因为ControlValueAccessor.writeValue
只将值写入到 VIEW 中,所以VIEW 的值和 MODEL 的值之间会有一个延迟。
值得一提的是,它将FormControl.setValue(val)
写入VIEW 和 MODEL这两个层,但如果我们要使用它,将会有一个无限循环,因为内部调用(因为必须更新 MODEL)和调用.val
setValue()
viewToModelUpdate
viewToModelUpdate
setValue()
让我们看看一个可能的解决方案:
ngOnInit () {
const initialOnChange = (this.ngControl.valueAccessor as any).onChange;
(this.ngControl.valueAccessor as any).onChange = (value) => initialOnChange(this.processInput(value));
}
使用这种方法,您在 VIEW 层修改数据,然后将其发送到ControlValueAccessor
.
我们可以确定它onChange
存在于每个内置的ControlValueAccessor
.
如果你要创建一个自定义的,只要确保它有一个onChange
属性。TypeScript 可以帮助你。
如果您想了解更多关于 Angular 的内部结构的信息@angular/forms
,我建议您阅读Angular Forms 的彻底探索。
推荐阅读
- python - 为什么我这么简单的线性回归不起作用
- c++ - c++ 方差和标准差
- javascript - 发送发布请求时,请求的资源上不存在“Access-Control-Allow-Origin”标头
- flutter - 在 Flutter 中获取两个同时屏幕交互的时间和位置信息
- json - 如何在 Laravel AuthServiceProvider 中返回 JSON?
- java - Java selemium Tor 启动失败
- javascript - 如何在 React (DatePicker) 中使用自定义日期字段
- javascript - OnClick 不起作用,但控制台中未出现错误
- javascript - 用于反应组件库的 cjs 或 esm
- javascript - 如果子元素包含某个类,如何将类设置为父元素?