angular: feat(Forms) Expose FormControl errors to parent FormGroup

I’m submitting a … (check one with “x”)

[ ] bug report
[x feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior When there are errors in a form group, the formGroup.status is ‘INVALID’ but the formGroup.errors property is null.

ex.

formGroup.controls.username.errors: {invalidEmail: true}
formGroup.errors: null

Expected/desired behavior When the validity of a FormControl changes, have the parent FormGroup.errors merged with the errors from the controls. This is similar to how the angularjs formController.$error property works. https://docs.angularjs.org/api/ng/type/form.FormController

What is the motivation / use case for changing the behavior? This would be helpful because I prefer to only disable a submit button when specific errors are on a form, such as required. With this I could drive that attribute off the formControl.errors value instead of formControl.status.

ex.

From:
<button [disabled]="myForm.status !== 'VALID'">Submit</button>

To: 
<button [disabled]="myForm.errors.required">Submit</button>

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 145
  • Comments: 28 (3 by maintainers)

Most upvoted comments

Any chance someone from the Angular team might acknowledge this issue before it’s four year anniversary?

+1 for this feature request.

I came across the same problem. But it’s not the first issue I have with forms…

I was really excited when reactive forms were released. But the more you use it in real world applications the more issues I encountered.

Is there some issue which contains all the current problems / feature requests / improvements for the forms module?

What I collected the last days:

  • This issue here. Something like this.form.hasError('required', { includeChildren: true })
  • Observables for touched, dirty, etc. #10887
  • Access to validators. Something like this.form.hasValidator('required'). ( see https://github.com/angular/material2/issues/2574 )
  • Warnings using Validators (basically the same as errors, but it doesn’t make the form invalid). ( #18195 ). Maybe even a more generic approach, so people can have normal errors which prevent submit, but also fatal errors which make it impossible to proceed, and warnings, and whatever they like.
  • Typed FormBuilder and FormGroup, FormArray, FormControl
  • And I guess there’s even more that would be really useful

Has there been any consideration on picking this up? I’d expect a lot of people working with complex forms are creating workarounds for this like we had to, effectively creating methods that recursively traverse the tree of controls looking at each control’s errors objects manually in order to create an aggregate object.

Other use case is that on a large form, with potentially tabs (which hides visually errors), it becomes unclear why a form is invalid and we might have to go through each tab to tackle the error. Hence having a summary of the errors at the top form level helps a lot making the form valid.

Hi, just want to provide an update: we’ve discussed this feature request with the team and the conclusion is that we’d like to add the support to gather errors for the entire control tree, but doing so would increase bundle size for all apps (since it’d need to be added to the AbstractControl-based classes and the code there is non-tree-shakable). We have another open ticket that talks about tree-shaking (see #39640) and we’d need to resolve it first before we can add extra logic to gather errors.

This feature request is in the Backlog now, but we don’t have any ETA at this moment.

It’d definitely be a nice thing to have that included in Angular itself.

In the meantime, we’ve created at work an open source library called ngx-sub-form (available here: https://github.com/cloudnc/ngx-sub-form) to be able to split our huge forms into smaller forms/components. One of the good part that is related to this thread? 😃

We do have access to all the nested errors 🎉

Example with that form, where Droid form and Assassin form are sub components:

image

And the errors that are accessible from the top level form component:

image

If you’re interested, you can find the live demo here: https://cloudnc.github.io/ngx-sub-form

And the source code of the demo here: https://github.com/cloudnc/ngx-sub-form/tree/master/src/app

I think that any way of implementing it would’ve been better than the current nothing, even if we don’t have a way to track back to the original invalid control

@NatoBoram hopefully that’ll help you 😃

Still no one from angular team interested in help for developers?

Recursive iterating over the FormGroup works for me:


  public group: FormGroup;
  
  public getErrors(group?: FormGroup | FormArray): { [key: string]: ValidationErrors } | ValidationErrors[] | null {
    const errors = group instanceof FormArray ? [] : {};

    if (group == null) {
      group = this.group;
    }

    let idx = 0;
    Object.keys(group.controls).forEach((name) => {
      idx++;
      const control = group.get(name);
      if (control instanceof FormArray || control instanceof FormGroup) {
        const tmpErrors = this.getErrors(control as (FormArray | FormGroup));
        if (tmpErrors != null) {
          errors[name] = tmpErrors;
        }
      } else if (control instanceof FormControl) {
        if (control.errors == null) {
          return null;
        }
        if (group instanceof FormArray) {
          (errors as ValidationErrors[])[idx] = control.errors;
        } else {
          errors[name] = control.errors;
        }
      }
    });

    return (group instanceof FormGroup && Object.keys(errors).length === 0)
      || Array.isArray(errors) && errors.length === 0 ? null : errors;
  }

@azhdarshirinzada your timeline

image

makes me think we’ve got more chance of seeing you post a comment notifying everyone here next year saying “*7 years” rather than getting a contribution from you to fix this issue. Feel free to prove me wrong though.

But as a reminder Angular is a free and open source project. You cannot have the same expectation as if you were paying something. Contribute yourself, pay a bounty for someone to raise a fix for you, or take a seat on the quiet bench of people kindly waiting for this to be solved.

We can all see the date of the issue and seeing comments like yours isn’t helping in any way.

Anyone moderating this thread feel free to mark my comment as off topic (and @azhdarshirinzada 's comment too while we’re at it…)

I don’t want to over-react, but recently I’ve started to vue things differently. Having started with Angular, I just assumed that all large open source projects just let heavily requested developer issues languish for years at a time. Come to find out, there are actually some large projects that actively work to resolve developer issues! They don’t even have to make announcements about finally committing enough resources to get their issue tracker under control, because as crazy as this sounds, they never ignored the issues in their issue tracker in the first place!. Apparently pull requests are not supposed to take years for approval either! Who knew? Don’t worry though, whenever you start to worry about Angular, just refer to their 2020 roadmap for reassurance.

+1

Any chance someone from the Angular team might acknowledge this issue before it’s four year anniversary?

let me fix the sentence: *six years 🦁

If you use a ControlValueAccessor implementation for your nested form, you can use the following workaround to propagate the validation state to the parent form by registering a validator on the parent form’s field:

export abstract class FormGroupComponent implements ControlValueAccessor, OnInit {

  public abstract readonly form: FormGroup;

  protected constructor(private readonly controlDir: NgControl | null) {
  }

  ngOnInit(): void {
    this.controlDir?.control?.setValidators(() => this.form.valid ? null : {});
  }

  [...]
}
  • form is the FormGroup containing the nested form’s model
  • controlDir is the form field of the parent form referencing the nested form (ca be injected using @Optional() @Self())

Despite the validation state, also the pristine and dirty state can be propagated to the parent form. A complete example of such a base class can be found here: https://gist.github.com/cbossi/0979c7dcdc2e215e28772a11ccc602ab

Got the same problem. It’s extremely confusing, I understand the need of distinguishing group errors, but there could be and extra property containing all the child errors… that would help.

Same for status, if there’s any child pending, the parent group should know about it.

For those following this issue, what would you think of providing this feature by providing errors via the unified control state event observable ? (related to #54579 & #48947)

@manklu That would work for something very simple. But in my case, the tabs are actually “dynamic”, in the sense it’s placed within a FormArray of unknown length, and there can even be tabs of tabs, or FormArray of FormArray. So at the end, it’s just one massive form where I need to have a “summary” of all errors.