Validation not updating in custom form controls using Angular's Control Value Accessor

Discussão em 'Angular' iniciado por Dominic, Janeiro 10, 2025.

  1. Dominic

    Dominic Guest

    I built a shared input component in Angular using Control Value Accessor interface. The input itself works but validation doesn't get updated when I change the value within the input. Here is the input component I created.

    import { Component, forwardRef, Input, OnInit } from '@angular/core';
    import {
    AbstractControl,
    ControlValueAccessor,
    FormsModule,
    NG_VALIDATORS,
    NG_VALUE_ACCESSOR,
    ValidationErrors,
    Validator,
    } from '@angular/forms';

    @Component({
    selector: 'app-input',
    standalone: true,
    imports: [FormsModule],
    templateUrl: './input.component.html',
    styleUrl: './input.component.scss',
    providers: [
    {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => InputComponent),
    multi: true,
    },
    {
    provide: NG_VALIDATORS,
    useExisting: forwardRef(() => InputComponent),
    multi: true,
    },
    ],
    })
    export class InputComponent implements ControlValueAccessor, Validator {
    @Input() value: string = '';
    @Input() type: string = 'text';
    @Input() placeholder: string = '';
    @Input() disabled: boolean = false;
    @Input() label: string = '';
    // default to a random id unless one is provided.
    @Input() id: string = '';

    isInvalid: boolean = false;
    touched: boolean = false;

    onChange = (value: string) => {};
    onTouched = () => {};
    onValidationChange = () => {};
    errorMessage: string = '';

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

    registerOnChange(onChange: any): void {
    this.onChange = onChange;
    }

    registerOnTouched(onTouched: any): void {
    this.onTouched = onTouched;
    }

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

    onInputChange($event: Event) {
    const input = $event.target as HTMLInputElement;
    this.value = input.value;
    this.onChange(this.value);
    this.markAsTouched();
    this.onValidationChange();
    }

    markAsTouched() {
    if (!this.touched) {
    this.onTouched();
    this.touched = true;
    }
    }

    validate(control: AbstractControl): ValidationErrors | null {
    this.errorMessage = this.getErrorMessage(control.errors);
    return control.errors;
    }

    registerOnValidatorChange(onValidationChange: () => void): void {
    this.onValidationChange = onValidationChange;
    }

    private getErrorMessage(errors: ValidationErrors | null): string {
    if (!this.touched || !errors) {
    return '';
    }
    if (errors['required']) {
    return 'This field is required';
    }
    if (errors['minLength']) {
    return `Minimum length is ${errors['minLength'].requiredLength}.`;
    }

    return '';
    }
    }



    When I log the control.errors within the validate method, it still shows that the required validation still has an error.

    I have a sample angular project showing this issue here:

    https://stackblitz.com/edit/stackblitz-starters-fy4zue?file=src/input/input.component.ts

    Things I have tried:

    • I changed [(ngModel)] two way binding and (ngModelChanges) listener to just a simple [value] and (input) listener but that did not help.
    • I registered the onValidationChange method but that did not work.

    Is there anything I am doing wrong, or something that I forgot to add, or is this an actual angular bug?

    Continue reading...

Compartilhe esta Página