Issue with the proper functionality of the this.formGroup.updateValueAndValidity() method in Angular 6

Currently, I am facing an issue where I need to add or remove validators in a formGroup's controls based on certain conditions. When I try to update the validators using `formGroup.updateValueAndValidity()` for the entire form, it does not seem to work as expected. However, if I individually apply the update for each control like `formGroup.get('formControl').updateValueAndValidity()`, it works fine but this approach doesn't seem ideal as I have to specify it for every control.


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

Although the above code snippet is functional, when I attempt to update all controls at once using

this.myForm.updateValueAndValidity();

it does not produce the desired result.

Answer №1

The updateValueAndValidity() method follows a bottom-up approach, meaning that when called on a control, it will only validate that specific control and its parent controls, not their children.

To understand how this method works in depth, you can refer to AbstractControl#updateValueAndValidity on GitHub.


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

Answer №2

Last week, I stumbled upon a similar situation and after some investigation, I concluded that this behavior is to be expected. The documentation clearly states:

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

It's important to note that it mentions updating "ancestors" rather than "descendants". So, when you execute updateValueAndValidity() on both control1 and control2, if they are valid, then myForm will also be marked as valid.

Answer №3

According to Serginho's explanation in the approved response, the issue lies in the fact that the updateValueAndValidity method works in a bottom-up manner and does not validate child controls. One potential remedy for this is creating a custom prototype method for the FormGroup class as shown below:

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

The validate method is recursive and triggers updateValueAndValidity for each leaf element (FormControl) within the tree structure of the form (FormGroup), including nested form groups.

Remember to import your module wherever you wish to utilize the validate method:

import '../core/extensions/formGroup';

Answer №4

NOTICE: While this approach may not align with best practices, it accomplishes what Angular restricts me from doing easily. Please note that this method is less efficient compared to Angular's native validation handling. Below is a technique to update the entire form validation:

  validateForm(control: AbstractControl) {
    control['controls'].forEach(
      // make sure to pass {onlySelf: true, emitEvent: false} 
      // or it will recurse indefinitely
      control => control.updateValueAndValidity({onlySelf: true, emitEvent: false})
    );

    return null;
  }

In my scenario, I required the complete form validity to be refreshed every time the user modifies any field (not limited to the current field's validation or only touched fields):

this.form.setValidators(this.validateForm);

Answer №5

I found @ssnake's solution to be helpful, however it did not have support for FormArray. I made some modifications to make it more versatile and independent of specific classes. Now, it checks if there are controls and validates each one, whether it is a FormControl, FormGroup, or FormArray:

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

AbstractControl.prototype.validate = function(): void {
  this.updateValueAndValidity();

  if ('controls' in this) {
    for (const key in this.controls) {
      if (this.controls.hasOwnProperty(key)) {
        this.get(key).validate();
      }
    }
  }
};

Answer №6

Give this a shot:

const formFields = [this.field1, this.field2, this.field3];

    for (let field of formFields) {
      for (let property in field.controls) {
        field.controls[property].updateValueAndValidity();
      }
    }

Answer №7

To ensure the validity of any FormControl, FormGroup, or FormArray, follow this method.

ValidateControl<T extends AbstractControl>(control: T): void {
  try {
    if (control instanceof FormGroup) {
      control.updateValueAndValidity()

      const controls = control.controls
      Object.keys(controls).forEach(key => {
        this.ValidateControl(controls[key])
      })
    }
    else if (control instanceof FormArray) {
      control.updateValueAndValidity()

      control.controls.forEach(formControl => {
        this.ValidateControl(formControl)
      })
    }
    else if (control instanceof FormControl) {
      control.updateValueAndValidity()
    }
    else {
      throw new Error('Error: unexpected control value')
    }
  } catch (error) {
    console.log(error)
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Instructions for opening a URL in a new tab using Angular

Within my Angular 10 application, I am conducting an API call that retrieves an external URL for downloading a pdf file. Is there a method to open this URL in a new browser tab without relying on the window object? I've been using window.open(url) suc ...

Form remains unchanged after manipulation in callback

Currently, I have a form with a list of editable objects (constants). Previously, it was possible to delete any object without confirmation. Now, I want to add a ngBootbox confirm step before the deletion process. In my .ts file, the code for deleting an ...

What is the method for placing a title in the initial column with the help of v-simple-table from Vuetify.js?

I am interested in using the v-simple-table UI component from Vuetify.js to create a table similar to the one shown below. https://i.sstatic.net/QNdpJ.png After creating the code in codesandbox and previewing the output, I noticed that the title is not a ...

Output Scalable Vector Graphics (SVG) content on a webpage

I need to include an SVG element in my Angular 2+ code. My goal is to provide users with the option to print the SVG element as it appears on the screen. <div class="floor-plan" id="printSectionId2" (drop)="onDrop($event)" (dragover)="onDragOver ...

Utilize Tailwind CSS in React to dynamically highlight the active navigation item on click

Check out my navigation bar code: <nav className="bg-white shadow dark:bg-gray-800"> <div className="container flex items-center justify-center p-6 mx-auto text-gray-600 capitalize dark:text-gray-300"> <Link ...

Is there an issue with integrating Bootstrap with Angular 6?

npm install bootstrap Update the angular.json file: "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "styles.scss" ] Include the Bootstrap CSS directly in your src/style.css: @import '~bootstrap/dist/css/bootstrap.min.css'; A ...

Angular response object being iterated through in a loop

I am facing a challenge while trying to iterate through an array containing values that need to be displayed to the user. Despite receiving a response with the data, I am having trouble accessing and looping through the elements of the array using Angular. ...

What is the reason behind the ineffectiveness of injection in abstraction?

I am working with an interface export interface Tree {} The base class implements this interface: export class TreeBase implements Tree {} There are several concrete classes that extend the TreeBase: export class TreeLayers extends TreeBase {} export cl ...

What is the best way to accept user input in typescript?

Currently, I am working on a TypeScript project that involves taking user input for the addition of two numbers. Below is the code snippet I am using: function rotatedString(S1,S2){ return S1+S2; } function processData() { //INPUT[uncomment & m ...

When trying to run "ng s -o/ng s/ng build" commands in Angular Cli, there is an issue where no output is displayed

Running "ng serve", "ng serve -o", and "ng build" does not display any output. I am in need of Angular for my project, so if anyone can offer assistance, please help me. https://i.sstatic.net/SmjaL.jpg ...

Deactivating a Component in Ionic Angular

I am interested in including a CanDeactivate Guard in my Ionic Angular project. After reading about the new "CanDeactivateFn" Guard, I have been unable to find any information or examples on how to implement it. Could someone provide me with an example? ...

When using Typescript type aliases, make sure to let Intellisense display the alias name instead of the source

Take a look at this brief code snippet type A = number; declare function f(): A; const a = f(); // `a` is number, not A What could be the reason for TS displaying a: number instead of a: A? ...

"Send the selected radio button options chosen by the user, with the values specified in a JSON format

My current task involves inserting radio button values into a MySql database using Angular. The form consists of radio buttons with predefined values stored in a json file. Below is an example of how the json file is structured: //data.json [{ "surve ...

Angular 10: Issue encountered while trying to instantiate an Object of an Object

export class School { constructor( public name: string, public address: Address, public district: string, public contactNumber: string, public gradeRange: string, public currentYear: number, ...

What steps can I take to ensure that Visual Studio replaces my JavaScript file during the TypeScript build process?

In my development process using Visual Studio to work on my TypeScript project, I have encountered an issue with the build process. I have a build.ts file that links to all of my other TS files and compiles them into one app.js file along with an associate ...

Mastering the Art of Adding Headers in Angular

Here is the Angular service code: getCareer(dataout: any){ let headers = new HttpHeaders().append('Content-Type', 'application/json').append('Authorization','Bearer'+' '+GlobalService.authToken); //hea ...

Enhance your React Highchart by incorporating gradient shading to the data points

One interesting feature in classic highcharts is the ability to apply gradient coloring to points: Highcharts.setOptions({ colors: Highcharts.getOptions().colors.map(function (color) { return { radialGradient: { cx: ...

Date selection feature in Material UI causing application malfunction when using defaultValue attribute with Typescript

Recently, I discovered the amazing functionality of the Material UI library and decided to try out their date pickers. Everything seemed fine at first, but now I'm facing an issue that has left me puzzled. Below is a snippet of my code (which closely ...

The compiler option 'esnext.array' does not provide support for utilizing the Array.prototype.flat() method

I'm facing an issue with getting my Angular 2 app to compile while using experimental JavaScript array features like the flat() method. To enable these features, I added the esnext.array option in the tsconfig.json file, so the lib section now includ ...

Error! Unexpected closure occurred while creating an Angular CLI project with npm

After cloning an Angular (4+) CLI project onto a new machine and running npm install, I encountered an error that reads as follows. This project works perfectly fine on other computers: npm ERR! premature close npm ERR! A complete log of this run can be ...