DEV Community

Jennifer Wadella for Bitovi

Posted on • Updated on

Understanding Angular's Control Value Accessor Interface

If you're dealing with forms in Angular on a regular basis, one of the most powerful things you can learn is how to use the Control Value Accessor interface. The CVA interface is a bridge between FormControls and their elements in the DOM. A component extending the CVA interface can create a custom form control that behaves the same as a regular input or radio button.

Why Would You Want to Use the Control Value Accessor Interface?

Sometimes you may need to create a custom form element that you want to be able to use as a regular FormControl. (For a better understanding of FormControls and other Angular Form classes you might want to read my article here) For example, creating a 5 star rating UI that updates a single value. We'll use this example in our demo.

star rating input

There's a lot happening in the UI here - stars changing colors as they're hovered over and displaying different text for each ratings, but all we care about is saving a number value 0-5.

Implementing the CVA

To use the CVA interface in a component, you must implement its three required methods: writeValue, registerOnChange, and registerOnTouched. There is also an optional method setDisabledState.

The writeValue method is called in 2 situations:

  • When the formControl is instantiated
rating = new FormControl({value: null, disabled: false}) 
  • When the formControl value changes

The registerOnChange method should be called whenever the value changes - in our case, when a star is clicked on.

The registerOnTouched method should be called whenever our UI is interacted with - like a blur event. You may be familiar with implementing Typeaheads from a library like Bootstrap or NGX-Bootstrap that has an onBlur method.

The setDisabledState method is called in 2 situations:

  • When the formControl is instantiated with a disabled prop
rating = new FormControl({value: null, disabled: false}) 
  • When the formControl disabled status changes

A star rating component implementing the CVA may look something like this:

export class StarRaterComponent implements ControlValueAccessor {
  public ratings = [
      stars: 1,
      text: 'must GTFO ASAP'
      stars: 2,
      text: 'meh'
      stars: 3,
      text: 'it\'s ok'
      stars: 4,
      text: 'I\'d be sad if a black hole ate it'
      stars: 5,
      text: '10/10 would write review on Amazon'
  public disabled: boolean;
  public ratingText: string;
  public _value: number;

  onChanged: any = () => {}
  onTouched: any = () => {}

  writeValue(val) {
    this._value = val;

  registerOnChange(fn: any){
    this.onChanged = fn
  registerOnTouched(fn: any){
    this.onTouched = fn

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;

  setRating(star: any) {
    if(!this.disabled) {
      this._value = star.stars;
      this.ratingText = star.text


You must also tell Angular that your component implementing the CVA is a value accessor(remember, interfaces aren't compiled in TypeScript) using NG_VALUE_ACCESSOR and forwardRef.

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

  selector: 'gr-star-rater',
  templateUrl: './star-rater.component.html',
  styleUrls: ['./star-rater.component.less'],
  providers: [     
      provide: NG_VALUE_ACCESSOR, 
      useExisting: forwardRef(() => StarRaterComponent),
      multi: true     
export class StarRaterComponent implements ControlValueAccessor {

Using Your New CVA Component

Now, to use your fancy new CVA component, you can treat is as a plain old FormControl.

this.galaxyForm = new FormGroup({
  rating: new FormControl({value: null, disabled: true})
<form [formGroup]="galaxyForm" (ngSubmit)="onSubmit()">
  <h1>Galaxy Rating App</h1>
  <div class="form-group">
      <gr-star-rater formControlName="rating"></gr-star-rater>
  <div class="form-group">
    <button type="submit">Submit</button>

Tada! Not so scary, huh? Questions or need help with Angular Reactive Forms? Let me know!

Top comments (6)

nickpea profile image

Thank Jennifer

I think its also good to clarify that the writeValue() CVA interface method isn't discharged after your onChanges() method is called. Meaning that the parent formControl (rating) doesnt update the child component property (value) when the formControl value is updated. It seemingly can only be done in the parent via calling the parent's formControl.setValue()/patchValue().

I think this is right, but please correct me because I'm making assumptions based on my own practice and I also noticed that right at the end of you child components model, the setRating() method does this manually rather than just call onChange() by itself.

No one explains this and I had to find out from looking at your code.

danielebarell profile image
Daniele Barell

Hi Jennifer.
Good article!
But I'd like to see also the template part of the StarRaterComponent.
Is it visible anywhere?

likeomgitsfeday profile image
Jennifer Wadella

Hi Daniele,

Here you go!

ren318 profile image

Good article. Wondering if the writeValue method should also invoke onChanged and onTouched.

lakerko profile image
Martin Ockovsky

I love your explanation! The listing of situations and such is very nice.
But you should include all the code, imports and template! :P

elazarza profile image

Very good jennifer! thanks!
You should show the child compoenent's template too, it was helpful seeing it the comments.