自定义表单控件(ControlValueAccessor)的绑定ngModel始终将表单设置为脏状态

Sta*_*eff 5 javascript typescript angular

我正在尝试实现一个非常简单的自定义表单控件。

@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; }
}
Run Code Online (Sandbox Code Playgroud)

但我的问题是,当我以(反应式)形式使用它时,只需设置[(ngModel)]评估form.dirtytrue,但如果我使用普通输入控件,一切都会按预期工作。

在此输入图像描述

@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; }
}
Run Code Online (Sandbox Code Playgroud)

我的实现有问题吗?如何让自定义控件以dirty仅在交互发生时才设置为 true 的方式工作?

Plunkr 复制:https://plnkr.co/edit/FBGAR4uqwen7nfIvHRaC ?p=preview

Sta*_*eff 4

编辑:经过一些更多的修改,我终于找到了我认为应该是正确的实现。

我不敢相信我根本没有在网上找到一个很好的解释,解释onChangewriteValue函数如何对应于脏/原始状态以及如何处理重置。

一旦您调用,onChange您的控件将被设置为dirty: true。调用form.reset()将重置此状态,但也会调用writeValue设置新值。所以永远不要调用onChange这个方法,因为它会破坏你的状态。

在我的实现中,我有一个简单的值设置器,我调用它来onChange实际设置正确的状态,因为该设置器仅由模板中的输入控件调用。

您的控件可能可以对值的实际更改方式进行更复杂的处理,但是只要您实际调用(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; }
}
Run Code Online (Sandbox Code Playgroud)