115

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!

Tobi
  • 2,591
  • 15
  • 34
Bonneville
  • 3,453
  • 4
  • 19
  • 20
  • 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: http://stackoverflow.com/questions/21370006/angular-conditional-email-validation – stephen.vakil Aug 27 '15 at 22:06
  • @stephen.vakil - I'm using angular2. – Bonneville Aug 28 '15 at 21:57
  • @Bonneville could you please explain how you are passing the checkbox state to the validator function? – Varun Mulloli Mar 07 '16 at 07:43

8 Answers8

104

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

starball
  • 20,030
  • 7
  • 43
  • 238
Nightking
  • 1,910
  • 1
  • 18
  • 22
  • Thanks @Nightking for the suggestion, I'll try it out. Note that your link isn't working. – Bonneville Nov 17 '15 at 01:52
  • @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. – Nightking Nov 18 '15 at 07:27
  • 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! – Bonneville Dec 11 '15 at 02:10
  • Thanks for your comment! Great that this works for you! Could you upvote or mark my answer? For my reputation :). – Nightking Dec 11 '15 at 10:17
  • 1
    Link is broken; may be updated to https://github.com/angular/angular/blob/master/packages/forms/src/model.ts#L413 although not quite the same. Could also link to Angular docs – Explosion Pills Dec 18 '17 at 18:21
  • 1
    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 – pravin Jan 30 '18 at 14:12
  • @pravin thank you. We had the same use case over here. I have to mention that maybe you have to go through all your controls on a FormGroup and call "updateValueAndValidity()", because this function only checks it's own validity and the one of it's parent. So if you call it on a FormGroup it just bubbles upwards and doesn't check other controls. Thought this could may be helpful :) – Nightking Jan 31 '18 at 14:58
  • Works very well, thx ! – Pedro Bezanilla Jan 28 '22 at 15:13
59

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)

Jay Byford-Rew
  • 5,736
  • 1
  • 35
  • 36
  • 2
    this is actually the right answer. If you want single input this.form.controls['name'].markAsTouched(); – chris_r Nov 21 '19 at 23:49
30

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

29

This did the trick for me

this.myForm.markAllAsTouched();
C.Ikongo
  • 1,706
  • 1
  • 16
  • 15
  • 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 Aug 04 '21 at 16:36
  • @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! – Mohammad Hossein Ganjyar May 31 '22 at 08:30
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
  });
}
jmreidy
  • 471
  • 4
  • 4
4

You can trigger validation in this way:

this.myform.get('myfield').updateValueAndValidity();
Carlos Cuesta
  • 1,262
  • 1
  • 17
  • 20
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();
  }
}
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);
        }
    };
}
pogiaron
  • 332
  • 3
  • 5