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

What is the best way to convert the text on a button into another language?

Currently, I am facing an issue with translating a button using Angular. The translation appears to be outside of the button in question. Additionally, there is another problem that I have encountered. For reference, here is a sample: https://i.stack.i ...

Creating a dynamic image binding feature in Angular 8

I am working with an object array that requires me to dynamically add an image icon based on the type of credit card. Typescript file icon: any; savedCreditCard = [ { cardExpiryDateFormat: "05/xx", cardNumberLast: "00xx", cardId: "x ...

Implementation of setProperties method on LineLayer in MapBox encounters resolution issues

I am currently utilizing the Android Mapbox SDK to integrate a VectorSource into a MapboxMap and then I am trying to incorporate a LineLayer onto the map as well. My version is 5.1.3 This code will be written in TypeScript due to its compatibility with t ...

I'm having trouble retrieving my variable within the socketcluster's socket.on function

How can I store the value of msg in the variable sample when sample is not accessible inside the callback function? import { Injectable } from '@angular/core'; import * as socketCluster from 'socketcluster-client'; @Injectable({ pro ...

Issue TS2339: The object does not have a property named 'includes'

There seems to be an issue that I am encountering: error TS2339: Property 'includes' does not exist on type '{}'. This error occurs when attempting to verify if a username is available or not. Interestingly, the functionality works ...

TypeScript failing to correctly deduce the interface from the property

Dealing with TypeScript, I constantly encounter the same "challenge" where I have a list of objects and each object has different properties based on its type. For instance: const widgets = [ {type: 'chart', chartType: 'line'}, {typ ...

What are some effective ways to manage repetitive HTML elements like headers and footers in Angular 4?

Within my Angular web project, I find myself using a variety of different headers on multiple pages. Is there a way to streamline this process by coding the header and footer once and having them automatically included in one or more pages? I am looking ...

Sending JSON data from Angular to WCF

Attempting to utilize the post method in Angular to send JSON data to a WCF service. The data is being sent in JSON format from Angular, however, the WCF service is receiving it as a null object. Is it possible to use the get method to send JSON data? Th ...

Issues with data binding in Angular2 are arising specifically in IE11

After successfully loading the application on Chrome, Firefox, and Edge, I encountered difficulties when trying to load it on IE11. The data bindings were not created properly, despite the internal data being fetched correctly through a websocket connectio ...

Angular and RxJS can be set up to delay the next call from BehaviorSubject in situations where there are multiple

Within my Angular application, I maintain a state using a service that stores the data as a BehaviorSubject. Whenever new data is received from the server, the next function is called to update the existing data and notify all subscribers. PseudoCode: ...

Bidirectional data binding in angular 12 reactive forms

After working with angular for a while, I encountered an issue while trying to implement two-way binding. The code snippet below is where I'm facing difficulty. Since the use of [(ngModel)] has been deprecated in Angular 12 within formGroup, finding ...

Discovering the name of an object property by locating its corresponding id

I am working with a basic JSON data structure where I need to retrieve the name from an object by comparing its ID. For example, if I have the number 2, I need to check if it matches any object's ID. If the ID is equal to 2, then I must fetch the corr ...

Having difficulty sending a message from an AngularJS application to an Angular 10 application through an iframe

I have two applications running locally - one is an AngularJS application and the other is an Angular 10 application. I am trying to access a page from the Angular 10 application using an iframe and send a message to it from the AngularJS application. How ...

Is there a method to ensure the strong typing of sagas for dispatching actions?

Within redux-thunk, we have the ability to specify the type of actions that can be dispatched enum MoviesTypes { ADD_MOVIES = 'ADD_MOVIES', } interface AddMoviesAction { type: typeof MoviesTypes.ADD_MOVIES; movies: MovieShowcase[]; } typ ...

Iterating over an object and inserting values into a JavaScript object using the ascending count as the identifier

Illustration: { Are you a coffee drinker?: yes, Do you like to exercise regularly?: no, How often do you eat out at restaurants?: 3 times a week, What is your favorite type of cuisine?: Italian } Results: {yes: 1, no: 1, 3 time ...

Disabling the scrollbar within angular elements

Trying to remove the two scrollbars from this code, but so far unsuccessful. Attempted using overflow:hidden without success filet.component.html <mat-drawer-container class="example-container" autosize> <button type="button&qu ...

Converting types to "any" and encountering the error message "There are two distinct types with the same name, but they are not related."

I am encountering some challenges while trying to use an NPM module that I developed along with its Typescript typings in another application. To simplify the examples, I will omit properties that are not relevant to the issue at hand. Within my module&ap ...

Validating the absence of a value in Angular 2: How to confirm if an object is

I am currently working with two objects. One object collects person details and the other one collects address details through a form. Each object contains 3 fields. Before sending this information to my parent, I need to check if these two objects are not ...

Exploring the Power of PrimeNG and Observables in Angular 4 with RxJS

After configuring my Angular 4 project with a service like this: const usersURL = 'http://my.super.url.php'; @Injectable() export class UserService { users: Observable<User[]> constructor (public http:Http) let tick$ = Observ ...

What led the Typescript Team to decide against making === the default option?

Given that Typescript is known for its type safety, it can seem odd that the == operator still exists. Is there a specific rationale behind this decision? ...