问题描述
问题与Formcontrol invalid state unchanged after setting value via valueAccessor.writeValue()相同。但是没有解决。您可以在他的帖子上回复。
就我而言,这很不错。我有电话号码的指示:
import { Directive,HostListener,ChangeDetectionStrategy } from '@angular/core';
import { NgControl } from '@angular/forms';
@Directive({
selector: '[phonemask]'
})
export class PhoneDirective {
constructor(public ngControl: NgControl) { }
@HostListener('ngModelChange',['$event'])
onModelChange(event){
this.onInputChange(event,false);
}
@HostListener('keydown.backspace',['$event'])
keydownBackspace(event){
this.onInputChange(event.target.value,true);
}
onInputChange(event,backspace){
let newVal = event.replace(/\D/g,'');
if (backspace && newVal.length <= 6) {
newVal = newVal.substring(0,newVal.length - 1);
}
if (newVal.length === 0) {
newVal = '';
} else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/,'$1');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,'$1-$2');
} else if (newVal.length <= 9) {
newVal = newVal.replace(/^(\d{0,'$1-$2-$3');
} else {
newVal = newVal.substring(0,9);
newVal = newVal.replace(/^(\d{0,'$1-$2-$3');
}
this.ngControl.valueAccessor.writeValue(newVal);
console.log(newVal);
console.log(this.ngControl.value)
}
}
问题是在某些情况下console.log(newVal); console.log(this.ngControl.value)具有不同的值。
例如
111-1
1111
111-111-111
111-111-1111
解决方法
引自this answer:
Angular具有某些元素的默认值访问器,例如input type='text'
,input type='checkbox'
等...
ControlValueAccessor
是VIEW层和MODEL层之间的中间人。当用户输入内容时,VIEW会通知ControlValueAccessor
,input
的工作是通知模型。
例如,发生onChange
事件时,将调用ControlValueAccessor
的{{1}}方法。 Here's how 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的值之间会有一个 delay 。
值得一提的是,FormControl.setValue(val)
会将val
写入和图层,但是如果我们使用它,将会有一个 infinite循环,因为setValue()
内部调用viewToModelUpdate
(因为必须更新MODEL),而viewToModelUpdate
调用setValue()
。
因此,可能的解决方案是将该代码段添加到您的指令中:
ngOnInit () {
const initialOnChange = (this.ngControl.valueAccessor as any).onChange;
(this.ngControl.valueAccessor as any).onChange = (value) => initialOnChange(this.makeChangesToInput(value));
}
@HostListener('ngModelChange',['$event'])
onModelChange(event){
this.ngControl.valueAccessor.writeValue(this.makeChangesToInput(event,false));
}
@HostListener('keydown.backspace',['$event'])
keydownBackspace(event){
this.ngControl.control.setValue(this.makeChangesToInput(event,true));
// if you want the `ngModelChange` handler from above to be called
// this.ngControl.control.setValue(this.makeChangesToInput(event,true),{ emitViewToModelChange: true });
}
makeChangesToInput(value,backspace){
let newVal = event.replace(/\D/g,'');
if (backspace && newVal.length <= 6) {
newVal = newVal.substring(0,newVal.length - 1);
}
if (newVal.length === 0) {
newVal = '';
} else if (newVal.length <= 3) {
newVal = newVal.replace(/^(\d{0,3})/,'$1');
} else if (newVal.length <= 6) {
newVal = newVal.replace(/^(\d{0,3})(\d{0,'$1-$2');
} else if (newVal.length <= 9) {
newVal = newVal.replace(/^(\d{0,'$1-$2-$3');
} else {
newVal = newVal.substring(0,9);
newVal = newVal.replace(/^(\d{0,'$1-$2-$3');
}
return newVal;
}
要点是,在将更改后的值发送到ControlValueAccessor
之前,应该在VIEW层上对输入值进行任何更改。