Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 6, this.formGroup.updateValueAndValidity() not working properly

I am trying to add and remove validators in a formGroup controls based on certain condition.

When I am updating the validators through formGroup.updateValueAndValidity() for whole form its not updating, where as if I am specifically applying for each Control i.e. formGroup.get('formControl').updateValueAndValidity(), it is working but i have to write for each control which is i hope not the correct way. What am i doing wrong?

if (data == 'x') {
    this.myForm.get('control2').setValue(null);
    this.myForm.get('control2').setValidators(Validators.nullValidator);
    this.myForm.get('control1').setValidators(Validators.required);
} else if (data == 'y') {
    this.myForm.get('control1').setValue(null);
    this.myForm.get('control1').setValidators(Validators.nullValidator);
    this.myForm.get('control2').setValidators(Validators.required);
}
this.myForm.get('control1').updateValueAndValidity();
this.myForm.get('control2').updateValueAndValidity();

this is working, but,

this.myForm.updateValueAndValidity();

this is not working.

like image 420
Ashutosh Singh Avatar asked Nov 13 '19 08:11

Ashutosh Singh


3 Answers

As Serginho said in the accepted answer, the problem is that updateValueAndValidity method is bottom-up, so it will not validate children controls. A possible solution for that is to write a custom prototype method for the FormGroup class like this:

import { FormGroup, FormControl } from '@angular/forms';

declare module '@angular/forms/forms' {
  interface FormGroup {
    validate(): void;
  }
}

FormGroup.prototype.validate = function(this: FormGroup): void {
  for (const key in this.controls) {
    const formElement = this.get(key);
    if (formElement instanceof FormControl) {
      formElement.updateValueAndValidity();
    } else if (formElement instanceof FormGroup) {
      formElement.validate();
    }
  }
};

validate is a recursive method that call updateValueAndValidity for each leaf (FormControl) of the tree (FormGroup), even in case of nested form groups.

Don't forget to import your module where you need to use the validate method:

import '../core/extensions/formGroup';
like image 180
ssnake Avatar answered Sep 29 '22 22:09

ssnake


updateValueAndValidity() is bottom-up, so if you call this method over a control, it will check only validations of this control and their parents, but not their children.

For more details, see AbstractControl#updateValueAndValidity on github to how it works.

  updateValueAndValidity(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
    this._setInitialStatus();
    this._updateValue();

    if (this.enabled) {
      this._cancelExistingSubscription();
      (this as{errors: ValidationErrors | null}).errors = this._runValidator();
      (this as{status: string}).status = this._calculateStatus();

      if (this.status === VALID || this.status === PENDING) {
        this._runAsyncValidator(opts.emitEvent);
      }
    }

    if (opts.emitEvent !== false) {
      (this.valueChanges as EventEmitter<any>).emit(this.value);
      (this.statusChanges as EventEmitter<string>).emit(this.status);
    }

    if (this._parent && !opts.onlySelf) {
      this._parent.updateValueAndValidity(opts);
    }
  }
like image 32
Serginho Avatar answered Sep 29 '22 23:09

Serginho


I stumbled upon this last week too, and I came to the conclusion that this is the expected behavior. The docs states the following:

By default, it also updates the value and validity of its ancestors.

Note that it says "ancestor" and not "descendants". This means that when you have run updateValueAndValidity() on control1 and control2, and they're both valid, myForm will be marked as valid too.

like image 40
ShamPooSham Avatar answered Sep 29 '22 22:09

ShamPooSham