6

I have an Angular 7 project for dynamically creating forms. I have one parent FormGroup with nested FormGroups of various types.

I want parentForm to be invalid until all of the nested/subforms are valid (actually want them submitted but haven't gotten there yet).

  this.parentForm = new FormGroup(this.subforms, { validators: allSubModulesValidValidator }); 

this.subforms is an object like this:

interface DynamicKeyFormGroup {
  [key: string]: FormGroup;
}

subforms: DynamicKeyFormGroup = {};

I know my validator is wrong, but I can't figure out how to design a validator for a FormGroup vs a FormControl.

The idea is that I'm trying to loop over all of this.subForms' properties which are the nested FormGroups and then checking their status. If any are invalid, mark parentForm as invalid.

const allSubModulesValidValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
  const controls = control.controls;
  for (const key in controls) {
    if (controls.hasOwnProperty(key)) {
      if (!(controls[key].status === 'valid')) {
        return { 'allSubModulesValid': true };
      }
    }
  }
  return null;
};

In response to comment. After removing the validator the parent is valid while child is invalid: child invalid, parent valid

azulBonnet
  • 831
  • 4
  • 14
  • 31

1 Answers1

0

This, in my opinion, is a bug in angular, although as seen in the comments above clearValidators it's the expected behavior.

So let's see what is happening.

I assume that you are clearing the validators (based on the image) either by doing

someControl.validator = null;

which is a bit inappropriate or by calling the clear validators function e.g.

someControl.clearValidators()

which according to the angular source does the following

  /**
   * Empties out the synchronous validator list.
   *
   * When you add or remove a validator at run time, you must call
   * `updateValueAndValidity()` for the new validation to take effect.
   *
   */
  clearValidators(): void {
    this.validator = null;
  }

as seen here GithubSource which is the same.

So the issue is that this status clearance doesn't update the status of the control and its parents.

In order to fix this behaviour after clearing the status you should update the control

someControl.clearValidators();
someControl.updateValueAndValidity();

updateValueAndValidity will update the current status of the control and will notify the parent FormGroup if any.

The example provided in the comments above by @jb-nizet: stackBlitz will work even if you clear the validators because he is calling the setValue method of the control, which does the following

      override setValue(value: TValue, options: {
        onlySelf?: boolean,
        emitEvent?: boolean,
        emitModelToViewChange?: boolean,
        emitViewToModelChange?: boolean
      } = {}): void {
        (this as {value: TValue}).value = this._pendingValue = value;
        if (this._onChange.length && options.emitModelToViewChange !== false) {
          this._onChange.forEach(
              (changeFn) => changeFn(this.value, options.emitViewToModelChange !== false));
        }
        this.updateValueAndValidity(options);
      }

as seen in Angular Source , so this will call internally updateValueAndValidity which will update the status.

3 years later captain here :D