angularvalidationangular-reactive-formsangular-forms

Issue with activating validators on Angular reactive form at runtime - using form.updateValueAndValidity()


I have a simple yet perplexing problem. I have a reactive form and I want to set validation programatically.

    this.boatForm.get('boatType').setValidators(Validators.required);
    this.boatForm.updateValueAndValidity();

    this.boatForm.get('boatType').updateValueAndValidity();
    this.#logger.debug(
        '/n', 'value:', this.boatForm.get('boatType').value,
        '/n', 'valid:', this.boatForm.get('boatType').valid
    )

The value of the boatType control is null. Therefore I expect the log to show:

value: valid: false

And... it does.

But I have multiple controls on that form so instead of applying updateValueAndValidity() to each control individually, I want to execute:

    this.boatForm.get('boatType').setValidators(Validators.required);
    // Set other validators on other controls on the same form
    this.boatForm.updateValueAndValidity();

    this.#logger.debug(
        '\n', 'value', this.boatForm.get('boatType').value,
        '\n', 'valid', this.boatForm.get('boatType').valid
    )

But now, I get:

value: valid: true

The work-around that we have implemented is to call a method:

static updateFormControlValidators(form: FormGroup): void {
    // Iterate over each control in the form group

    Object.keys(form.controls).forEach(controlName => {
        const control = form.get(controlName);

        // Call updateValueAndValidity to re-evaluate the validity of each control
        if (control) {
            control.updateValueAndValidity();
        }
    });
}

But the question is:

Should form.updateValueAndValidity() accomplish the same thing?


Solution

  • The docs mention:

    updateValueAndValidity:

    Recalculates the value and validation status of the control. By default, it also updates the value and validity of its ancestors.


    To further support this, the documentation of AbstractControl mentions to run the control's updateValueAndValidity and not the root form.

    AbstractControl - Docs (addValidators)

    Sets the synchronous validators that are active on this control. Calling this overwrites any existing synchronous validators.
    When you add or remove a validator at run time, you must call updateValueAndValidity() for the new validation to take effect.
    If you want to add a new validator without affecting existing ones, consider using addValidators() method instead.


    So when you update the form using updateValueAndValidity, the form is the root ancestor, hence the child control is not updated.

    this.boatForm.updateValueAndValidity();
    

    When we update the control using updateValueAndValidity, the control gets the required validation applied and the root form also has it's recalculation of the value and validation status.

    this.boatForm.get('boatType')!.updateValueAndValidity();
    

    Conclusion:

    So running the form's updateValueAndValidity will update the validators of the root form and not the children.

    Reference Stackblitz