Search code examples
javascripthtmlangularangular-forms

Angular validate ControlValueAccessor with ReactiveForms


I was using ControlValueAccessor to create an custom input in Angular. I recognized that the validation of the FormControls stoped working.

Here is a simple example I made on Stackblitz.

@Component({
  selector: 'app-text-field',
  templateUrl: './text-field.component.html',
  standalone: true,
  imports: [CommonModule, FormsModule],
})
export class TextFieldComponent implements ControlValueAccessor, OnInit {
  protected control = inject(NgControl); // control should be injected

  onChange(value: string) {}
  onTouch() {}

  value = '';

  ngOnInit(): void {
    this.control.valueAccessor = this;
  }

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

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

  writeValue(value: string) {
    this.value = value;
  }
}

I can't provide NG_VALIDATORS as it creates a circular dependency and couldn't find any code that is still usable in Angular 16+ or could be considered as best practice.

So what is the best way to support validation?

Edit1:

Here is also the way the input is used:

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, TextFieldComponent],
  template: `
    <form [formGroup]="form">
      <app-text-field formControlName="a" />
    </form>
    <p>{{ form.errors }}</p>
  `,
})
export class App {
  form = new FormGroup({
    a: new FormControl<string>('', {
      validators: [
        Validators.required,
        Validators.maxLength(8),
        Validators.minLength(2),
      ],
    }),
  });

  constructor() {
    this.form.valueChanges.subscribe((val) =>
      console.log(val, this.form.errors)
    );
  }
}

Solution

  • As Chellappan pointed out in the comments, the code is just fine. The ControlValueAccessor already gets validated by the FormGroup. There is currently a bug that errors of FormControls don't get exposed to the FormGroup, what led to a missunderstanding.

    https://github.com/angular/angular/issues/10530