124

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!

3
  • Are you just trying to enable/disable the validation based on the value of X? What sort of validators are you using? You can cause validators to execute based on a condition in your scope, but I'm not sure if that approach will work for you. See: stackoverflow.com/questions/21370006/… Commented Aug 27, 2015 at 22:06
  • @stephen.vakil - I'm using angular2. Commented Aug 28, 2015 at 21:57
  • @Bonneville could you please explain how you are passing the checkbox state to the validator function? Commented Mar 7, 2016 at 7:43

9 Answers 9

110

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

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks @Nightking for the suggestion, I'll try it out. Note that your link isn't working.
@Bonneville Thanks for the information. They extracted the form code to the common namespace. Things changing a bit to fast :). I've updated the link to the source.
I finally got around to using this piece of code and it seems to be working for me. Thanks! BTW, there is a typo in your code: the letter 'e' is missing in the updateValueAndValidity() function. Your code has updat instead of update. Cheers, this was a big help!
Link is broken; may be updated to github.com/angular/angular/blob/master/packages/forms/src/… although not quite the same. Could also link to Angular docs
Life saver!! The use case that I was stuck was when the user submits a form without touching a single form control. The form is invalid but the controls where not showing any error message
|
62

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)

1 Comment

this is actually the right answer. If you want single input this.form.controls['name'].markAsTouched();
34

This did the trick for me

this.myForm.markAllAsTouched();

2 Comments

The problem with marking as touched is that if you are tracking changes, like to tell them there are unsaved changes upon leaving, then this will trigger that logic
@stevec I didn't have understand your comment! Could you leave a demo here?! I'm using of markAllAsTouched method, whereas I updateValueAndValidity method didn't work for me to re-validate my inputs!
33

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

Comments

6

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

Comments

5

You can trigger validation in this way:

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

Comments

2

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

Comments

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.