angular icon indicating copy to clipboard operation
angular copied to clipboard

Problem with nested ControlValueAccessor and Validation

Open GonzaloCorchon opened this issue 2 years ago • 4 comments

Which @angular/* package(s) are the source of the bug?

forms

Is this a regression?

No

Description

In Reactive Forms, if you have two or more nested custom form components that implement ControlValueAccessor and Validator, the outer one doesn't update its validation status until you make a change in the inner component.

It seems the validation runs initially only once from the outer component to the inner one, so if the inner component initially is invalid the outer doesn't know anything until the next validation cycle.

In the example provided, initially "user-data" is invalid, so "complex-form" should be also invalid, but as you can see it updates its status once you start typing in the input fields.

status

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/angular-ivy-nkr2be?file=src/app/complex-form.component.ts

Please provide the exception or error you saw

No response

Please provide the environment you discovered this bug in

Angular 12.0.5

Anything else?

No response

GonzaloCorchon avatar Jul 10 '21 19:07 GonzaloCorchon

if you have two or more nested custom form components

I'm curious: what's the use case?

lazarljubenovic avatar Jul 18 '21 17:07 lazarljubenovic

Imagine a huge form splitted in many subcomponents that are resused on other places of your application. For example, You could have a component responsible for editing a list of things, and that list could be part of a form with many more fields apart from that list. Each thing of that list could be another custom component, so you end up with a tree of form components.

GonzaloCorchon avatar Jul 20 '21 07:07 GonzaloCorchon

That should be a reusable form group. I don't think it makes sense to put fields inside fields.

lazarljubenovic avatar Jul 20 '21 15:07 lazarljubenovic

This is a subtle one. It will likely take a couple hours to really figure out why this is happening at initialization.

dylhunn avatar Jul 21 '22 23:07 dylhunn

I prepared an example with FormGroups as @lazarljubenovic suggested, but the behaviour with wrong validation state in the root form on initialization/page refresh is the same as @GonzaloCorchon already pointed out: https://stackblitz.com/edit/angular-ivy-juakju

However, the issue only seems to be present, when the form nesting level is greater than one - as showcased in my example.

bfoese avatar Sep 26 '22 01:09 bfoese

On both stackblitz above, it seems that calling updateValueAndValidity in the CVA hierarchy's deepest component's ngAfterViewInit lifecycle could be a temporary alternative.

lorpmtp avatar Oct 10 '22 19:10 lorpmtp

is there any update on this? I don't think reusable formGroup is a valid solution here. It should be possible to have multiple levels of CVAs without any fancy logic behind it. And currently it works for all scenarios - except one - initialisation of all formControls. So update from bottom to top works - when user updates controls, everything bubbles up to the parent form. But if the child is invalid at the form creation, this calls validate() methods from top to bottom, and it doesn't update the parent components properly.

St1c avatar Oct 19 '22 14:10 St1c

I made a repository to demonstrate the difference between nested CVA and FormGroups: https://github.com/Julien-Marcou/nested-cva-validation

The bug is still present in Angular v17.0.3

IMO it's still not a good idea to nest CVA inside each other, as it's not working properly with markAllAsTouched() (https://github.com/angular/angular/issues/10887) and it's harder to control the state of sub control (enabled/disabled/touched/retrieve error of a specific control) and requires some hacky directive or other things to make it work properly.

But there might have some cases where it makes sense, so I think this validation bug is definitely something that should be looked into by the Angular team 🙏

Julien-Marcou avatar Nov 18 '23 16:11 Julien-Marcou

What's really missing is a warning in the docs not to nest CVAs and the official guide for a reusable form group. Even better, a runtime warning.

It just doesn't make sense with the current semantics of @angular/forms at all.

It should be possible to have multiple levels of CVAs (https://github.com/angular/angular/issues/42815#issuecomment-1284115159)

Why do you think that?

But there might have some cases where it makes sense

Got any examples? Even contrived ones?

lazarljubenovic avatar Nov 18 '23 19:11 lazarljubenovic

I'm also running into this situation as well. My use case is that I have a CVA component comprised of several inputs for entering phone numbers. This phone component is then used to build another CVA that allows dynamically adding/removing these phone CVAs within a form array. Having these be separate components compartmentalizes the logic and gives more flexibility depending on the situation.

@dylhunn Do you have any updates on this issue? Or any recommendations for how we can deal with this issue in the meantime?

Shane-E avatar Dec 19 '23 20:12 Shane-E