Understanding angular ControlValueAccessor with an example
Tags: webdev, angular, javascript, programming
Published: 2023-05-09 00:16:26

In my previous [article](https://dev.to/krishhnaa/understanding-angular-controlvalueaccessor-482h), I explained the ControlValueAccessor Interface now lets deep dive with an example.

Let’s say you have a custom input component that is meant to accept phone numbers in a specific format, such as `(123) 456–8799`. You want to create a custom form control that will enforce this format and allow for easy data binding to the component.

To achieve this, you can create a ControlValueAccessor for the custom input component. The ControlValueAccessor interface defines four methods that you need to implement: `writeValue()`, `registerOnChange()`, `registerOnTouched()`, and `setDisabledState()`.

Here’s an example implementation of the `ControlValueAccessor` interface for our custom phone number input component:

```
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-phone-number-input',
  templateUrl: './phone-number-input.component.html',
  styleUrls: ['./phone-number-input.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => PhoneNumberInputComponent),
      multi: true
    }
  ]
})
export class PhoneNumberInputComponent implements ControlValueAccessor {
  myForm! : FormGroup;
  private onChange: (value: string) => void;
  private onTouched: () => void;

  public phoneNumber: string;
   
   constructor(private fb: FormBuilder) { 
    this.myForm = this.fb.group({
       // implement the formcontrol
    })
   }
  public writeValue(value: string): void {
    this.phoneNumber = value;
  }

  public registerOnChange(fn: (value: string) => void): void {
    this.onChange = fn;
  }

  public registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  public setDisabledState?(isDisabled: boolean): void {
    // Implement if needed
  }

  public onPhoneNumberChanged(): void {
    // enforce the phone number format
    const formattedPhoneNumber = this.phoneNumber.replace(/[^0-9]/g, '').replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
    this.phoneNumber = formattedPhoneNumber;
    
    // notify the form control that the value has changed
    this.onChange(formattedPhoneNumber);
  }

  public onBlur(): void {
    this.onTouched();
  }
}
```
In the above code, we first import `ControlValueAccessor` and `NG_VALUE_ACCESSOR` from the @angular/forms module. We then implement the ControlValueAccessor interface in our PhoneNumberInputComponent class.

The `writeValue()` method is called by the Angular forms API when the value of the form control is updated. We simply set the phoneNumber property to the value passed in.

The `registerOnChange()` and `registerOnTouched()` methods are used to register callback functions that Angular calls when the value or touched state of the control changes. We store these callback functions in private class properties `onChange` and `onTouched`.

The `setDisabledState()` method is optional and can be implemented if you want to disable the input control.

The `onPhoneNumberChanged()` method is called whenever the phone number input value changes. We enforce the phone number format by replacing all non-numeric characters and formatting the string with parentheses and hyphens. We then call the onChange() method with the formatted phone number to notify the form control that the value has changed.

The `onBlur()` method is called whenever the input control loses focus. We call the `onTouched()` method to notify the form control that the input has been touched.

By implementing the `ControlValueAccessor` interface, our custom phone number input component can be easily used in a reactive form. In the above code, we first import `ControlValueAccessor` and `NG_VALUE_ACCESSOR` from the `@angular/forms` module. We then implement the ControlValueAccessor interface in our `PhoneNumberInputComponent` class.

The `writeValue()` method is called by the Angular forms API when the value of the form control is updated. We simply set the phoneNumber property to the value passed in.

The `registerOnChange()` and `registerOnTouched()` methods are used to register callback functions that Angular calls when the value or touched state of the control changes. We store these callback functions in private class properties onChange and onTouched.

The `setDisabledState()` method is optional and can be implemented if you want to disable the input control.

The `onPhoneNumberChanged()` method is called whenever the phone number input value changes. We enforce the phone number format by replacing all non-numeric characters and formatting the string with parentheses and hyphens. We then call the onChange() method with the formatted phone number to notify the form control that the value has changed.

The `onBlur()` method is called whenever the input control loses focus. We call the onTouched() method to notify the form control that the input has been touched.

By implementing the `ControlValueAccessor` interface, our custom phone number input component can be easily used in a reactive form.

And in the template, you will create a form with `FormGroup` where you will specify the component and use the `app-phone-number-input` like the below example.

```
<form [formGroup]="myForm">
  <label for="phone">Phone number:</label>
  <app-phone-number-input formControlName="phone"></app-phone-number-input>
</form>
```
In the above code, we first create a `FormGroup` in our component's class and assign it to the formGroup attribute of the `<form>` element.

We then create a label for the phone number input and use the `formControlName` directive to bind the phone number input to the "phone" form control.

Finally, we use the `<app-phone-number-input>` custom input component and pass it the "phone" form control.

Now, whenever the phone number input value changes, the `onPhoneNumberChanged()` method in our `PhoneNumberInputComponent` will be called, and the value will be propagated to the "phone" form control through the `onChange()` callback.

Similarly, whenever the input loses focus, the `onBlur()` method will be called, and the onTouched() callback will be called to mark the control as touched.

Note that you also need to import the `ReactiveFormsModule` in your module in order to use reactive forms in your application