我试图实现一个非常简单的自定义表单控件。
@Component({
selector: 'text-input',
template: '<input [(ngModel)]="value" />',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TextInputComponent),
multi: true
}]
})
export class TextInputComponent implements ControlValueAccessor {
private _value: any;
get value(): any { return this._value; }
set value(value: any) { return this.writeValue(value); }
writeValue(value: any) {
if (value !== this._value) {
this._value = value;
this.onChange(value);
}
}
onChange = (x: any) => { };
onTouched = () => { };
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
但我的问题是,当我以(被动)形式使用它时,只需设置[(ngModel)]
评估form.dirty
到true
,但如果我使用普通输入控件,一切都按预期工作
<h2>Hello {{text}}</h2>
<form #f="ngForm" >
<text-input [(ngModel)]="text" name="text"></text-input>
</form>
Dirty: {{f.dirty}}
<form #f2="ngForm" >
<input [(ngModel)]="text" name="text" />
</form>
Dirty: {{f2.dirty}}
我的实施有问题吗?如何让我的自定义控件以dirty
仅在交互发生时设置为true的方式工作?
Plunkr再现:https://plnkr.co/edit/FBGAR4uqwen7nfIvHRaC?p=preview
答案 0 :(得分:1)
编辑:经过一些修修补补后,我终于找到了我认为应该是正确实施的内容。
我无法相信我从未在网上找到一个很好的解释onChange
和writeValue
函数如何对应脏/ prestine状态以及如何处理重置。
只要您致电onChange
,您的控件就会设置为dirty: true
。调用form.reset()
将重置此状态,但也会调用writeValue
来设置新值。所以从不在此方法中调用onChange
,因为它会破坏您的状态。
在我的实现中,我为我的值设置了一个简单的setter,我在其中调用onChange
来实际设置正确的状态,因为setter仅由我模板中的输入控件调用。
您的控件可能会更复杂地处理值的实际更改方式,但只要您实际调用onChange
,如果您想将更改反映到外部,您就可以设置值使用writeValue
控制而不更改状态一切都会正常工作。
@Component({
selector: 'text-input',
template: `<input [(ngModel)]="value"
(blur)="onTouched()" />`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TextInputComponent),
multi: true
}]
})
export class TextInputComponent implements ControlValueAccessor {
private _value: any;
// NOTE: you could also just bind to a normal field without
// getters and setters and call onChange from your template
// e.g. from (ngModelChange) of the input control
get value(): any { return this._value; }
set value(value: any) {
this.writeValue(value);
this.onChange(value);
}
writeValue(value: any) {
if (value !== this._value) {
this._value = value;
}
}
onChange(x: any) { console.log(x); }
onTouch() { };
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}