我正在使用 angular 8 制作指令来进行一些处理并将文本转换为大写。简化代码如下:
html:
<input class="form-control" id="label" name="label" required myDirective>
Run Code Online (Sandbox Code Playgroud)
指示:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
现在,视图已正确更新,但是模型晚了一步。例如:如果输入文本显示“AAAA”,ng-reflect-model则将显示“AAAa”。
我在stackblitz中重现了错误:Error Reproduced in Stackblitz
知道我哪里错了吗?
先谢过!
And*_*tej 10
/* ... */
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));
}
Run Code Online (Sandbox Code Playgroud)
让我们看看为什么它最初不起作用。
角有默认值存取某些元素,如input 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);
});
}
Run Code Online (Sandbox Code Playgroud)
神奇发生在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;
}
Run Code Online (Sandbox Code Playgroud)
dir.viewToModelUpdate(control._pendingValue);是调用ngModelChange自定义指令中的事件。这意味着模型值是来自输入的值(小写)。并且因为ControlValueAccessor.writeValue 只将值写入 VIEW,所以VIEW 的值和 MODEL 的值之间会有延迟。
值得一提的是,FormControl.setValue(val)将写入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));
}
Run Code Online (Sandbox Code Playgroud)
使用这种方法,您可以在 VIEW 层修改数据,然后再将其发送到ControlValueAccessor.
我们可以确定onChange存在于每个内置的ControlValueAccessor.
如果你要创建一个自定义的,只要确保它有一个onChange属性。TypeScript 可以帮助您解决这个问题。
如果您想阅读更多关于 的内部结构@angular/forms,我建议您查看对 Angular Forms 的彻底探索。
你可以使用它
@HostListener('input', ['$event'])
ngModelChange(event: any) {
const item = event.target
const value = item.value;
const pos = item.selectionStart;
this.control.control.setValue(this.processInput(value), { emit: false });
item.selectionStart = item.selectionEnd = pos
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们使用 @HostListener 输入来获取项目,而不仅仅是值。这允许我们在更改值后将光标定位在他的位置
注意:要制作简单的大写,最好使用 css text-transform:uppercase ,当我们想要获取值时,使用 toUpperCase()
注意2:关于掩码请参阅此SO
| 归档时间: |
|
| 查看次数: |
732 次 |
| 最近记录: |