Get Off My Lawn
Get Off My Lawn

Reputation: 36311

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 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 });

Working example

Upvotes: 0

Views: 1422

Answers (3)

Oded BD
Oded BD

Reputation: 3276

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();
    }

Upvotes: 0

Batajus
Batajus

Reputation: 6257

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);

Upvotes: 1

inorganik
inorganik

Reputation: 25525

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.

Upvotes: 1

Related Questions