我正在尝试实现此指令(https://plnkr.co/edit/yftpJ4A1HwrKkpfcSIT7?p=preview):
export class PrevValDirective {
constructor(private valueAccessor: DefaultValueAccessor, private model: NgModel) {
valueAccessor.registerOnChange = (fn: (val: any) => void) => {
valueAccessor.onChange = (val) => {
if(val === 'false') {
return fn(model.value);
}
return fn(val);
};
}
}
}
但是通过实现DefaultValueAccessor / ControlValueAccessor(https://plnkr.co/edit/Vu9OJBmqhw8W5Rt9AIOJ?p=preview):
export class PrevValDirective implements DefaultValueAccessor {
constructor(private model: NgModel){
}
registerOnChange(fn: (val: any) => void) {
this.onChange = (val) => {
if(val === 'false') {
model.control.setValue(model.value);
return;
}
return fn(val);
};
}
}
出于某种原因 - registerOnChange
不会触发。
我还想将onChange
更改为常规(非匿名)函数,但无法弄清楚如何(我需要fn
var)。
答案 0 :(得分:3)
您需要实施自定义ControlValueAccessor
,而不是扩展DefaultValueAccessor
。 https://github.com/angular/angular/issues/9146#issuecomment-241191899
要实施ControlValueAccessor
,您应该为此指令提供NG_VALUE_ACCESSOR
令牌。最简单的解决方案可能如下所示:
import {
Input, forwardRef, Renderer2,
Injector, ElementRef, Directive
} from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
export const PREV_VAL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PrevValDirective),
multi: true
};
@Directive({
selector: '[ngModel][prevVal]',
host: {
'(input)': 'handleInput($event.target.value)',
'(blur)': 'onTouched()',
},
providers: [PREV_VAL_VALUE_ACCESSOR]
})
export class PrevValDirective implements ControlValueAccessor {
@Input() prevVal: string;
onChange = (_: any) => {};
onTouched = () => {};
constructor(
private _renderer: Renderer2,
private _elementRef: ElementRef,
private _inj: Injector) {
}
writeValue(value: any): void {
const normalizedValue = value == null ? '' : value;
this._renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue);
}
registerOnChange(fn: (_: any) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
}
handleInput(value: any): void {
if (value === this.prevVal) {
let model = this._inj.get(NgControl); // get NgModel
model.control.setValue(model.value); // set previous value
return;
}
this.onChange(value);
}
}
很遗憾,您无法在实施NgControl
的课程中注入NgModel
(ControlValueAccessor
)。它会上升错误
无法实例化循环依赖! NgControl
幸运的是,您可以利用Injector
来获取它,就像我在此行所做的那样
let model = this._inj.get(NgControl);
我也将prevValue
传递给@Input
<input type="text" [(ngModel)]="val" prevVal="false">
<强> Plunker Example 强>