Bonneville
Bonneville

Reputation: 3583

How to trigger Form Validators in Angular2

In angular2 I want to trigger Validators for some controls when a another control is changed. Is there some way that I can just tell the form to re-validate? Better still, can I request validation of specific fields?

Example: Given Checkbox X and input P. Input P has a validator that behaves differently based on the model value of X. When X is checked/unchecked I need to invoke the validator on P. The Validator on P will look at the model to determine the state of X and will validate P accordingly.

Here's some code:

constructor(builder: FormBuilder) {
    this.formData = { num: '', checkbox: false };

    this.formGp = builder.group({
        numberFld: [this.formData.num, myValidators.numericRange],
        checkboxFld: [this.formData.checkbox],
    });
}

this.formGp.controls['checkboxFld'].valueChanges.observer({
    next: (value) => {
        // I want to be able to do something like the following line:
        this.formGp.controls['numberFld'].validator(this.formGp.controls['numberFld']);
    }
});

Anybody have a solution? Thanks!

Upvotes: 124

Views: 131645

Answers (9)

Arsen Khachaturyan
Arsen Khachaturyan

Reputation: 8320

To achieve this, we need to call the markAsTouched() method over FormControl.
But what if the control itself is actually FormGroup?

Here is the code handling all possible nestings:

triggerFormValidation(formGroup: any = this.myCreateForm): void {
    for (const key in formGroup.controls) {
        const control = formGroup.controls[key];

        if (!control.controls) { // FormControl case
            control.markAsTouched(); // trigger changes check
        } else { 
            this.triggerFormValidation(control); // FormGroup case
        }
    }
}

Upvotes: 0

Nightking
Nightking

Reputation: 2258

I don't know if you are still looking for an answer, so here is my suggestions:

Have a look at this: Angular 2 - AbstractControl

I think what you could do is following:

this.formGp.controls['checkboxFld'].valueChanges.observer({
    next: (value) => {
       this.formGp.controls['numberFld'].updateValueAndValidity();
    }
});

This should trigger and run the validators. Furthermore the state gets updated as well. Now you should be able to consult the checkbox value within your validator logic.

Validaton-Guide

FormControl Documentation

Upvotes: 110

Gabriel Guerrero
Gabriel Guerrero

Reputation: 496

Here is another similar way that also uses markAsDirty and updateValueAndValidity, particularly good if you use angular material where markAsTouched is not enough.

export function forceValidation(form: AbstractControl) {
  if (form instanceof FormGroup || form instanceof FormArray) {
    for (const inner in form.controls) {
      const control = form.get(inner);
      control && forceValidation(control);
    }
  } else {
    form.markAsDirty();
    form.markAsTouched();
    form.updateValueAndValidity();
  }
}

Upvotes: 2

Carlos Cuesta
Carlos Cuesta

Reputation: 1464

You can trigger validation in this way:

this.myform.get('myfield').updateValueAndValidity();

Upvotes: 5

C.Ikongo
C.Ikongo

Reputation: 1856

This did the trick for me

this.myForm.markAllAsTouched();

Upvotes: 34

Michika Iranga Perera
Michika Iranga Perera

Reputation: 1596

With the help of this blog

blog link

I have came across a solution with the combine of Nightking answer

Object.keys(this.orderForm.controls).forEach(field => {
       const control = this.orderForm.get(field);
       control.updateValueAndValidity();

});

this.orderForm is the form group

Upvotes: 33

pogiaron
pogiaron

Reputation: 342

static minMaxRange(min: number, max: number): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
        if (Validators.min(min)(control)) { // if min not valid
            return Validators.min(min)(control);
        } else {
            return Validators.max(max)(control);
        }
    };
}

Upvotes: 0

jmreidy
jmreidy

Reputation: 471

There are more elegant ways of modeling this behavior - for example, putting your state into a ReplaySubject and observing that, and then using async validators observing the state - but the pseudo-coded approach below should work. You simply observe the value changes in the checkbox, update the model as appropriate, then force a re-validation of the numberFld with the updateValueAndValidity cal.

constructor(builder: FormBuilder) {
  this.formData = { num: '', checkbox: false };
  const numberFld = builder.control(this.formData.num, myValidators.numericRange);

  const checkbox = builder.control(this.formData.checkbox);
  checkbox.valueChanges.map(mapToBoolean).subscribe((bool) => {
    this.formData.checked = bool;
    numberFld.updateValueAndValidity(); //triggers numberFld validation
  });

  this.formGp = builder.group({
      numberFld: numberFld,
      checkboxFld: checkbox
  });
}

Upvotes: 6

Jay Byford-Rew
Jay Byford-Rew

Reputation: 6014

with my ControlGroup I do this because I have errors divs checking if touched

for (var i in this.form.controls) {
  this.form.controls[i].markAsTouched();
}

(this.form is my ControlGroup)

Upvotes: 62

Related Questions