Angular: Cross Field Validation Not Working with setError()
I Have a Cross Field Validation Method That Should Fail If One of the Following Evaluates to True: Country == 'United States' and stateSelect Is Empty Country!...
I have a cross field validation method that should fail if one of the following evaluates to true:
country == 'United States'andstateSelectis emptycountry != 'United States'andstateTextis empty
country and stateSelect are both <select> items while sateText is an <input type="text">. When I load the page country = 'United States' and stateSelect = {name: 'Alabama', abbr: 'AL'}, and everything evalues properly.
If I change country to another value in the select, then change it back to United States, it evaluates properly, but my button stays disabled.
<form [formGroup]="purchase" class="forum-purchase__grid">
<!-- inputs and selects -->
<button [disabled]="purchase.invalid">Next</button>
</form>
The component looks like this:
public constructor() {
this.purchase = new FormGroup({
stateSelect: new FormControl(this.selectedState),
stateText: new FormControl(''),
country: new FormControl(this.selectedCountry, [Validators.required]),
}, { validators: [this.stateRegionValidator] });
}
private stateRegionValidator(formGroup: FormGroup): ValidationErrors | null {
const stateSelect = formGroup.get('stateSelect');
const stateText = formGroup.get('stateText');
const country = formGroup.get('country');
// Validate the fields
if (country.value === 'United States' && !stateSelect.value) {
stateSelect.setErrors({ 'badState': true });
return { 'badState': true };
} else if (country.value !== 'United States' && !stateText.value.trim().length) {
stateText.setErrors({ 'badRegion': true });
return { 'badRegion': true };
}
// The validation was successful.
return null;
}
I do notice that this works if I remove these two lines, however, I don't get error styles on the inputs then:
stateSelect.setErrors({ 'badState': true });
stateText.setErrors({ 'badRegion': true });
3 Answers
So your button is still disabled, because you did not remove the previously set errors from your different form controls.
When you add following above your return null; it will work:
stateSelect.setErros(null);
stateText.setErrors(null);
The issue is what you called out - you need to remove the setErrors calls. Those are side effects of your group validator which validates the form as a whole. When those set errors on the individual fields, nothing can unset them.
You should probably write custom validators for each field that use the logic in your group validator function, while not calling setErrors and just returning null or a value. Or keep the group validator, and show a single error for when the group has an error.
In some cases, I was needed to do something like this. listen on value change:
<select [(ngModel)]="selectedCountry" (ngModelChange)="test($event)" formControlName="country">
and in TS:
countryUpdate($event: string) {
if ($event == 'United States') {
this.purchase.controls.stateText.clearValidators();
} else {
this.purchase.controls.stateText.setValidators([this.stateRegionValidator]);
}
this.purchase.updateValueAndValidity();
}