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%2Finput%2Finput.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?