Skip to solution
hardFrontend

How do you build a custom form control by implementing the ControlValueAccessor interface?

994 views
01

Understand the problem

Explain how to hook up custom UI components to Reactive Forms.

formscustom-control
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Implement the ControlValueAccessor interface by defining writeValue(), registerOnChange(), registerOnTouched(), and optionally setDisabledState(). Register the component as an NG_VALUE_ACCESSOR provider in the component's providers array.

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

Implementing a custom star-rating form control
import { Component, Provider, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

const STAR_PROVIDER: Provider = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => StarRatingComponent),
  multi: true
};

@Component({
  selector: 'app-star-rating',
  standalone: true,
  providers: [STAR_PROVIDER],
  template: '<div class="stars">' +
    '  @for (star of [1, 2, 3, 4, 5]; track star) {' +
    '    <span (click)="rate(star)">{{ rating >= star ? \'★\' : \'☆\' }}</span>' +
    '  }' +
    '</div>'
})
export class StarRatingComponent implements ControlValueAccessor {
  rating = 0;
  onChange = (val: number) => {};
  onTouched = () => {};
  disabled = false;

  writeValue(value: number): void {
    this.rating = value || 0;
  }

  registerOnChange(fn: any): void { this.onChange = fn; }
  registerOnTouched(fn: any): void { this.onTouched = fn; }
  setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled; }

  rate(val: number) {
    if (!this.disabled) {
      this.rating = val;
      this.onChange(val);
      this.onTouched();
    }
  }
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 88 of 121 decoded in the Angular track. One more won't hurt.

Back to track