[angular] Fix for custom angular formcontrol retaining input value after form reset
Published: 2024-03-12
I came across a behavior in Angular where a custom formcontrol (using ControlValueAccessor
) retained its value in the UI after being reset.
Credit to this stackoverflow question, which helped me fix this issue.
My post gives a concise description, fix and explanation for this behavior.
The behavior
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputRetainsComponent),
multi: true,
}],
template: `
<input
[value]="value"
(input)="onChange($event)"
/>`,
})
export class InputRetainsComponent implements ControlValueAccessor
A component that implements ControlValueAccessor
implements a couple of methods to interact with the Angular forms API.
onChange(event: Event): void {
const value: string = (<HTMLInputElement>event.target).value;
this.changed(value);
}
A changed
method is registered as callback in this component.
Whenever the value in the ui changes the method is called and the form is notified through the callback.
writeValue(value: string): void {
this.value = value;
}
On the other side you have a writeValue
function that the form uses to write the value to the custom formcontrol.
Now if you write anything in your input in the ui and call setValue
, the ui will update.
However, when you call .reset()
on the form the input will retain its value.
The fix
The solution is to write the value directly to the view, you don’t need to keep an internal value
;
template: `
<input #inputRef />`,
...
writeValue(value: string): void {
const ref = this.inputRef();
ref && (ref.nativeElement.value = value);
}
The explanation
An answer is actually in the comments of the ControlValueAccessor
interface.
@usageNotes
### Write a value to the element
The following example writes a value to the native DOM element.
```ts
writeValue(value: any): void {
this._renderer.setProperty(this._elementRef.nativeElement, 'value', value);
}
Though it may appear strange at first, writing directly to the DOM is actually encouraged. Maybe custom form controls are excluded from normal change detection
It’s still strange behavior though, the first solution only had an issue when the reset function was called. There is a mention in the MDN docs that the value attribute is used for initial value. But when using normal input binding, the value is also mirrored…
If you want to look at full code examples you can find them here.