Angular ControlValueAccessor Explained
The ControlValueAccessor interface offers a collection of functions that enable Angular forms to communicate with native DOM elements. In essence, it serves as a vital link between Angular Form APIs and DOM elements.
interface ControlValueAccessor {
writeValue(obj: any): void
registerOnChange(fn: any): void
registerOnTouched(fn: any): void
setDisabledState(isDisabled: boolean)?: void
}To illustrate where ControlValueAccessor can be useful, let's consider an application that involves forms with custom build select boxes, checkboxes or date fields. In such a scenario, the traditional approach would be to rely on component interaction methods like @Input and @Output to handle these elements. However, using ControlValueAccessor provides a more streamlined solution by enabling direct communication between the Angular Form APIs and the DOM elements, eliminating the need for intermediary component communication.
Implementing ControlValueAccessor
The following code snippet illustrates a component that extends the ControlValueAccessor interface:
import { Component, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-my-element',
templateUrl: './my-element.component.html',
styleUrls: ['./my-element.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: MyElementComponent,
multi: true,
},
],
})
export class MyElementComponent implements OnInit, ControlValueAccessor {
value: string = '';
onChange: (value: string) => void;
onTouched: () => void;
constructor() {}
// We have to define the following functions to use ControlValueAccessor
writeValue(obj: any): void {
// assigning the value from the control value
this.value = obj;
}
registerOnChange(fn: any): void {
this.onChange = fn;
// this function has to be called from the code
// whenever the value in the code is updating
// example: this.onChange(newValue)
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
// this function has to be called from the code
// to update the touched field in the control
// similar to registerOnChange function
// example: this.onTouched()
}
setDisabledState(isDisabled: boolean): void {
// this will get called if the control
// is being disabled
}
ngOnInit(): void {}
}Then in the parent component, you can bind a form control directly:
<!-- parent component -->
<app-my-element
formControlName="your form control name goes here">
</app-my-element>As shown in the above code, it is possible to bind a form control directly to the MyElementComponent, allowing any value changes made in the component to be automatically reflected in the form control.
And that's about it!
Thanks for reading and until the next time.