Angular20 min read
Custom Form Controls with ControlValueAccessor
Integrate custom UI widgets (date pickers, rating stars) with Angular forms like built-in inputs.
Grace Thompson
August 6, 2025
9.1k244
ControlValueAccessor (CVA) lets you build a custom input component that works with Angular forms.
Example custom controls:
- star rating
- phone input with country code
- date picker wrapper
## Goal: Star rating input that works with formControlName
### Step 1: Create a rating component
```ts
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-rating',
standalone: true,
imports: [CommonModule],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RatingComponent),
multi: true,
},
],
template: `
<div class="stars">
<button type="button" *ngFor="let s of stars" (click)="set(s)">
{{ s <= value ? '★' : '☆' }}
</button>
</div>
`,
})
export class RatingComponent implements ControlValueAccessor {
stars = [1, 2, 3, 4, 5];
value = 0;
disabled = false;
private onChange: (v: number) => void = () => {};
private onTouched: () => void = () => {};
writeValue(v: number): void {
this.value = v ?? 0;
}
registerOnChange(fn: (v: number) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
set(v: number) {
if (this.disabled) return;
this.value = v;
this.onChange(v);
this.onTouched();
}
}
```
### Step 2: Use it in a reactive form
```html
<form [formGroup]="form">
<app-rating formControlName="rating"></app-rating>
</form>
```
Now your custom component behaves like a native input.
> Next: State management options (Signals store patterns, NgRx, Akita style decisions).
#Angular#Forms#Advanced