I have a simple Angular (v8.0) component which just contains an input with the required
attribute set. I have a template driven form which contains that component and binds it to an initial value. When the app starts the status of the input is valid, because it's bound to an initial value. But the form is incorrectly invalid. After making any change to the input value the form status is then correct, and remains correct from then on.
I'm returning the input's validation status in my validate
method implementation and this seems to be the source of the problem. It works properly if I return an object I construct myself based on the current value of the control. However I would rather avoid this because I'll need other validation states in future and I don't want to have manually check for them all.
I've tried adding calls to updateValueAndValidity
at various places in the code but this has no effect.
How can I make the initial validation status of the form take into account the status of the nested custom component?
Here's the code for the component:
import { AfterViewInit, Component, ViewChild } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl, Validator, NgModel } from "@angular/forms";
@Component({
selector: "my-input",
template: '<input [(ngModel)]="value" required #model="ngModel" />',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: MyInputComponent,
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: MyInputComponent,
multi: true
}
]
})
export class MyInputComponent implements ControlValueAccessor, Validator {
onChange: any = () => {};
onTouch: any = () => {};
@ViewChild("model", { static: true }) model: NgModel;
private _value: string = "";
set value(x: string) {
if (this._value !== x) {
this._value = x;
this.onChange(x);
}
}
get value() {
return this._value;
}
writeValue(obj: any): void {
this.value = obj;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
validate(c: FormControl) {
return this.model.errors;
//return this.value ? null : { required: true }; // this works!
}
}
And here's the code for the outer form which contains it:
import { Component } from "@angular/core";
@Component({
selector: "my-app",
template: `
<form #frm="ngForm">
<my-input [(ngModel)]="myValue" name="myInput"></my-input>
</form>
`
})
export class AppComponent {
myValue: string = "hello";
}
Here's a StackBlitz: https://stackblitz.com/edit/angular-lp3xpa. Add or remove a character to the input and the form status changes to valid.