angular icon indicating copy to clipboard operation
angular copied to clipboard

Reactive form control `root` points to nothing or itself.

Open liesahead opened this issue 1 year ago • 2 comments

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

forms

Is this a regression?

No

Description

We need to have a validator for simple FormControl to compare multiple FormGroup values as well. Trying to reach that in two ways:

  1. Use control.parent.parent.parent (doesn't look good);
  2. Use control.root.get(*path*).

However, both options have bug. Let's talk about latter one. root is either falsy or equal to control (points to itself) value. So, we had to add such workaround

    // In case form hasn't initialized
    if (control.root == null || control.root === control) {
        return null;
    }

    *Validation logic here*

But that doesn't work as expected because now form controls aren't validated on form init because of such workaround. How can we avoid such behavior?

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

No response

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 14.0.4
Node: 14.17.5
Package Manager: yarn 1.22.18
OS: win32 x64

Angular: 14.0.4
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, language-service, material, platform-browser
... platform-browser-dynamic, router

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.1400.4
@angular-devkit/build-angular      14.0.4
@angular-devkit/build-ng-packagr   0.1002.0
@angular-devkit/core               14.0.4
@angular-devkit/schematics         14.0.4
@angular/flex-layout               14.0.0-beta.40
@schematics/angular                14.0.4
rxjs                               7.5.5
typescript                         4.6.3

Anything else?

No response

liesahead avatar Jul 18 '22 20:07 liesahead

Hello! I am not entirely clear on what you're trying to do. Would you be able to provide a reproduction and example on StackBlitz?

dylhunn avatar Jul 18 '22 23:07 dylhunn

@dylhunn , here https://stackblitz.com/edit/angular-wkpmz4?file=src/app/app.component.ts

This is the best I could achieve in short amount of time. If you comment these lines validator won't work anymore image

And in our case (not in the repro) when items are added with some delay the validator doesn't work correctly initially for some reason..

Also, if you remove 1 from form field, the error will disappear, but if you enter it again the error won't get back. The state will remain valid.

liesahead avatar Jul 19 '22 07:07 liesahead

I was trying to analyse this issue and I think this is not a bug (however, I don't think this particular case is mentioned in docs). I've also noticed that AbstractControl.root doesn't seem to have unit tests that would cover only this feature (but it might be covered by other unit tests).

AbstractControl.root always returns a value. Either itself or its root control so it should never be false or null (although it might (?) in the past since this issue was created for Angular 12). The docs isn't very clear because it doesn't mention that root can also return the control itself if there're no parents.

https://github.com/angular/angular/blob/15.0.4/packages/forms/src/model/abstract_model.ts#L1262-L1270

What happens in the stackblitz above is that it creates FormControls with validators like:

new FormGroup({
  key: new FormControl(itemKey, uniqueKeyValidator),
  somethingElse: new FormControl(),
})

This means that it first creates a form control new FormControl(itemKey, uniqueKeyValidator) that executes the validation function and then adds the control into its parent group FormGroup. In other words, the first validation is executed before the FormControl is added to FormGroup and that's why control.root === control.

If you re-run the validation with something like f.get('g1.0.c1')?.updateValueAndValidity(); the control c1 will have correct parent.

Updated demo for Angular 15: https://stackblitz.com/edit/angular-wkpmz4?file=src%2Fapp%2Fapp.component.scss,tsconfig.json,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.module.ts,src%2Fapp%2Fapp.component.ts

martinsik avatar Dec 17 '22 19:12 martinsik

control has a property "parant" and you can use parent instead of root https://angular.io/api/forms/FormControl

// I think this could works if (control.parent == null || control.parent === control) { return null; }

mpeguero avatar Mar 05 '23 14:03 mpeguero