When working with forms in angular a normal approach is to just have the form elements (like inputs) directly in the form
<form>
<input>
</form>
When this form is submitted and the input has a validator eg. required. the form inputs are validated and if it's not valid we can show that to the user... great...
To be able to reuse custom inputs we can create a component that contains this input + extras.
<form>
<custom-component>
</form>
Check this stackblitz: https://stackblitz.com/edit/components-issue-whxtth?file=src%2Fapp%2Fuser%2Fuser.component.html
When hitting the submit button only one input is validated. If you interact with both inputs they will validate
There is nothing strange as far as I can see in the CVA component.
@Component({
selector: "user",
templateUrl: "./user.component.html",
styleUrls: ["./user.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent implements ControlValueAccessor {
constructor(@Optional() @Self() public ngControl: NgControl) {
if (this.ngControl != null) {
// Setting the value accessor directly (instead of using the providers) to avoid running into a circular import.
this.ngControl.valueAccessor = this;
}
}
onTouched = (_value?: any) => {};
onChanged = (_value?: any) => {};
writeValue(val: string): void {}
registerOnChange(fn: any): void {
this.onChanged = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
}
Like I said the main reason why your input doesn't react to the validation is your component configured as changeDetection: ChangeDetectionStrategy.OnPush
this will make your component only react to restricted actions :
So you have 2 options :
Here how that workaround is :
constructor(
@Optional() @Self() public ngControl: NgControl,
private cdr: ChangeDetectorRef // import the CDR
) {
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
const { _parent } = this.ngControl as any; // this is not a public property
// and refers to the parent form
_parent.ngSubmit.subscribe((r: any) => { // this is fired when the form submitted
this.cdr.detectChanges(); // detect changes manually
});
}
}
This will work where you have a form as the parent. With some null checks, it will work consistently. But you may encounter some other scenarios where you may not have the opportunity to trigger manual change detection and it will hurt the reusability of your component badly.