I have a nested form component with an <input>
field set up to work with formControlName="nested"
. The validators are set on the parent FormControl
, like so:
form = this.fb.group({
value: ['', [Validators.required, Validators.minLength(3)]],
nested: ['', [Validators.required, Validators.minLength(3)]],
});
I want to propagate the status from the parent FormControl
to the nested <input>
so it reacts in the same way as a regular, non-nested <input>
, i.e. when just touching it and clicking submit (which does control.markAsTouched()
), the status is set to INVALID and the CSS style ng-invalid
is set.
I managed to get a partial win after reading this SO post with the following code that subscribes to status changes of the parent control, but "touching" the nested input will revert it to VALID and clicking Submit will not set the ng-invalid
style.
@ViewChild('tbNgModel', {static: false}) tbNgModel: NgModel;
private isAlive = true;
constructor(@Optional() @Self() public ngControl: NgControl, private cdr: ChangeDetectorRef) {
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
}
ngAfterViewInit(): void {
this.ngControl.statusChanges.pipe(
takeWhile(() => this.isAlive)
).subscribe(status => {
console.log('Status changed: ', status, 'Errors: ', this.ngControl.errors);
this.tbNgModel.control.setErrors(this.ngControl.errors);
this.cdr.detectChanges();
});
this.ngControl.control.updateValueAndValidity(); // to force emit initial value
}
Stackblitz reproduction of my problem
How can I truly propagate the status to the nested control, all while setting the validators only on the parent FormControl
?
Final Solution
From @Eliseo's answer and this other SO post, here is the implementation I ended up doing (it works for me, but there may be a better way still)
component.ts
constructor(@Optional() @Self() public ngControl: NgControl) {
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
}
...
getValidationCss() {
if (!this.ngControl) return {};
return {
'ng-invalid': this.ngControl.invalid,
'is-invalid': this.ngControl.invalid && this.ngControl.touched,
'ng-valid': this.ngControl.valid,
'ng-touched': this.ngControl.touched,
'ng-untouched': this.ngControl.untouched,
'ng-pristine': this.ngControl.pristine,
'ng-dirty': this.ngControl.dirty,
};
}
component.html
...
<input #tb class="form-control" [ngClass]="getValidationCss()" ...>
...