Sanju Sudheer
Back to blog

Angular ControlValueAccessor Explained

·2 min read
angularfrontendtypescript

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.