Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →NG_VALUE_ACCESSOR is the Angular dependency-injection token that registers a ControlValueAccessor so Angular Forms can connect a control to its view. The accessor carries values in two directions: writeValue sends a form-model value to the view, while the callback saved by registerOnChange sends a user edit back to the model. A custom control also needs to report touched state and, when supported, reflect disabled state.
What NG_VALUE_ACCESSOR does
Angular defines ControlValueAccessor as “a bridge between the Angular forms API and a native element in the DOM.” NG_VALUE_ACCESSOR is the token through which an accessor is provided for Angular’s form directives to find. This is what lets a component participate in reactive forms with formControl or formControlName, and template-driven forms with ngModel.
As an Amazon Associate I earn from qualifying purchases.
The accessor separates two directions of synchronization. A form-model update flows into the component through writeValue. A user interaction flows back through the function Angular supplies to registerOnChange. Angular’s forms guide describes this propagation in both reactive and template-driven forms.
How the four ControlValueAccessor methods work
| Method | Direction or purpose | What the component should do |
|---|---|---|
writeValue(value) |
Model to view | Update the displayed or internal value from Angular. Do not treat this as a user edit or call the registered change callback from it. |
registerOnChange(fn) |
View to model | Save Angular’s callback and invoke it when user interaction changes the control’s value. |
registerOnTouched(fn) |
Touched-state notification | Save the callback and call it when the control is blurred or otherwise should count as touched. |
setDisabledState(isDisabled) |
Disabled-state update | When implemented, apply the status to the actual interactive UI, not merely to a visual label. |
The distinction between writeValue and the change callback prevents a feedback loop: Angular can update the view without the component reporting that update as a fresh user input. Touched and disabled states are separate from the value itself.
#1 Best Overall
Register a custom accessor with NG_VALUE_ACCESSOR
For a conventional custom component, provide the component itself under NG_VALUE_ACCESSOR. Use useExisting so Angular reuses the component instance, and multi: true so the provider joins the available accessor providers.
import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-rating',
template: `
<button type="button"
[disabled]="disabled"
(click)="choose(1)"
(blur)="markTouched()">1</button>
<button type="button"
[disabled]="disabled"
(click)="choose(2)"
(blur)="markTouched()">2</button>
`,
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RatingComponent),
multi: true
}]
})
export class RatingComponent implements ControlValueAccessor {
value: number | null = null;
disabled = false;
private onChange: (value: number | null) => void = () => {};
private onTouched: () => void = () => {};
writeValue(value: number | null): void {
this.value = value;
}
registerOnChange(fn: (value: number | null) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
choose(value: number): void {
if (this.disabled) return;
this.value = value;
this.onChange(value);
}
markTouched(): void {
this.onTouched();
}
}
This example shows the contract rather than a complete rating widget: a real control would render its selected value and expose its actual interaction semantics. The key points are that model writes only update component state, user choices call the saved change callback, and blur reports touched state.
Rank #2
Choose the default accessor or a custom one
Use Angular’s default accessor for text-like behavior
Angular’s DefaultValueAccessor handles ordinary text inputs and textareas. The ngDefaultControl directive can also be used on a custom element whose value behavior is similar and does not need special processing. This is appropriate only when the element’s interaction and value semantics match the default behavior.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Implement a custom accessor for custom value semantics
If a widget has a distinct value representation or interaction—such as a rating picker, range control, or composite input—implement ControlValueAccessor and register it. For select controls, Angular’s SelectControlValueAccessor supports compareWith to customize how option identity is compared, which is useful when options are objects rather than primitive values.
Rank #3
Handle “No value accessor” errors
If Angular reports that no value accessor is available for a form control, check the connection from the form directive to the control rather than changing the value logic at random:
- Confirm the element is inside the expected form context and uses the intended directive, such as
formControl,formControlName, orngModel. - For a custom component, confirm it implements
ControlValueAccessorand that its provider usesNG_VALUE_ACCESSOR,useExisting, andmulti: true. - Confirm the relevant Angular Forms package is imported in the application or feature context where the control is used.
- If the element is a basic text-like custom element without special semantics, consider
ngDefaultControl; do not use it to mask a genuinely custom interaction model. - If the component injects its own
NgControlas part of an Angular Material custom form field, check the version-specific case below before adding a provider.
Angular Material custom form fields and the NgControl cycle
The Angular Material v9 guide documents a narrow exception: a custom form-field component that injects its own NgControl can create a cyclic dependency if it also provides NG_VALUE_ACCESSOR. That guide’s workaround is to assign the accessor directly when the control exists:
Rank #4
if (ngControl != null) {
ngControl.valueAccessor = this;
}
This is not a general replacement for the provider pattern. It addresses the particular injection arrangement documented in the Angular Material v9 custom form-field guide. Because that guide is for v9, verify the guidance against the Angular Material version and component architecture in your project.
Signal Forms are a separate custom-control route
For Angular Signal Forms, the migration guide describes custom-control interfaces such as FormValueControl and FormCheckboxControl. It explicitly says not to implement those interfaces together with ControlValueAccessor on the same component. Treat this as a separate forms architecture rather than adding both integration mechanisms to one control; see the Signal Forms migration guide.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




