I have a reactive form with two fields.First is custom input using ControlValueAccessor, and last is just regular HTML input.
Problem is, after performing form.reset(), the value of custom input is retained event its value in reactive form is null already.
As you can see in image, the first time I input and clear the values, it is working well.
But, as second time and onwards, the input value is STILL retained in custom input component. While, the normal HTML input is cleared and working well regardless of how many times I click Clear Input. Can you help me, please? Did I miss to put something?
Files:
Here is the form:
ts file
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
firstName: [{ value: '', disabled: false }],
lastName: [{ value: '', disabled: false }],
});
}
clearInput() {
this.form.reset();
}
}
html file:
<form [formGroup]="form">
<app-custom-input formControlName="firstName"></app-custom-input>
<input formControlName="lastName" placeholder="Last name" />
<button (click)="clearInput()">Clear Input</button>
</form>
<br />
<pre>{{ form.value | json }}</pre>
Here is the custom input file:
ts file:
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true,
},
],
})
export class CustomInputComponent implements ControlValueAccessor {
value: string;
changed: (value: any) => void;
touched: () => void;
isDisabled: boolean;
writeValue(value: string): void {
this.value = value;
}
registerOnChange(fn: any): void {
this.changed = fn;
}
registerOnTouched(fn: any): void {
this.touched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.isDisabled = isDisabled;
}
onChange(event: Event): void {
const value: string = (<HTMLInputElement>event.target).value;
this.changed(value);
}
}
html file:
<input
placeholder="First name"
[disabled]="isDisabled"
[value]="value"
(input)="onChange($event)"
(blur)="touched()"
/>
Ok, you doing things well, but, you have to research a little bit deeper about this problem. If you go through HTML specification, you may find that the value
attribute for the input
html element is just an initial value. And that's why you get only first change if you push the reset button (actually you assign value there and writeValue
method invokes).
So the solutions are several, the simplest and relative to your code style is to get the reference to the input and assign value manually:
custom-input.component.html
<input
placeholder="First name"
[disabled]="isDisabled"
(input)="onChange($event)"
(blur)="touched()"
#inputRef
/>
custom-input.component.ts
export class CustomInputComponent implements ControlValueAccessor {
@ViewChild('inputRef')
inputRef: ElementRef<HTMLInputElement>;
changed: (value: any) => void;
touched: () => void;
isDisabled: boolean;
writeValue(value: string): void {
if (this.inputRef) {
this.inputRef.nativeElement.value = value;
}
}
...
Another solution is to use ngModel
and then it can work with value
property binded to the input.