javascript - Özel form denetiminin (ControlValueAccessor) ngModel bağlaması, formu her zaman kirli duruma ayarlıyor
Çok basit bir özel form denetimi uygulamaya çalışıyorum.
@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; }
}
Ama benim sorunum, bunu bir (reaktif) formda kullandığımda sadece [(ngModel)] ayarlamak form.dirty'yi true olarak değerlendirmesine neden oluyor, ancak normal bir giriş kontrolü kullandığımda her şey beklendiği gibi çalışıyor.
<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}}
Uygulamamda herhangi bir problem var mı? Özel kontrolümün, sadece bir etkileşim gerçekleştiğinde dirty değerinin true olarak ayarlanacağı şekilde çalışmasını nasıl sağlayabilirim?
Plunkr çoğaltma: https://plnkr.co/edit/FBGAR4uqwen7nfIvHRaC?p=preview
Cevap #1
DÜZENLEME: Biraz daha uğraştıktan sonra sonunda doğru uygulama olması gerektiğine inandığım şeyi buldum.
İnternette onChange ve writeValue fonksiyonlarının kirli/temiz durumla nasıl ilişkilendiğini ve sıfırlamaların nasıl ele alındığını iyi bir şekilde açıklayan bir şey bulamamış olmama inanamıyorum.
onChange öğesini çağırır çağırmaz, kontrolünüz dirty: true olarak ayarlanacaktır. form.reset() çağırmak bu durumu sıfırlayacak, ancak aynı zamanda yeni değeri ayarlamak için writeValue öğesini de çağıracaktır. Bu yüzden bu yöntemde onChange öğesini ASLA çağırmayın çünkü durumlarınızı bozacaktır.
Uygulamamda, değerim için basit bir set ediciye (setter) sahibim ve burada onChange çağırarak doğru durumu (state) gerçekten ayarlıyorum, çünkü set edici yalnızca şablonumdaki giriş kontrolü tarafından çağrılıyor.
Kontrolünüz değerin aslında nasıl değiştirileceğine dair daha karmaşık bir işlem yapabilir, ancak değişikliği dışarıya yansıtmak istiyorsanız gerçekten onChange'i çağırmanız ve kontrolünüzün değerini durumu değiştirmeden writeValue ile ayarlayabilmeniz sürece her şey düzgün çalışacaktır.
@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; }
}
