I am building a nested, dynamic Form where the User has a group, and then can nest conditions within that group, or new additional group objects within a group
FormArray. Here is what the basic UI looks like. Note, not all the pieces are working, but for now I am trying to add a nested group. A nested group will work for the FormBuilder
, but it gives an error and does not show correctly on the UI. The error is: ERROR Error: Cannot find control with path: 'statement -> groups -> 0 -> groups -> conditions'
. Before going further, the StackBlitz can be found HERE
The form object looks like this:
{
"statement": {
"groups": [
{
"conjunctor": null,
"conditions": [
{
"variable": ""
}
],
"groups": []
}
]
}
}
Within the statement → groups → groups
the user is able to push an additional FormGroup
that will contain a "groups" object:
{
"conjunctor": null,
"conditions": [
{
"variable": ""
}
],
"groups": []
}
Long term, I expect to be able to push additional groups and further nest this Form, but for now, I am trying to get it to work on the UI. The HTML is as shown below and in this StackBlitz. I continue to get the error:
ERROR Error: Cannot find control with path: 'statement -> groups -> 0 -> groups -> conditions'
, and based off several S.O. examples, I recognize that this error is due to the way my HTML is nested and the FormGroups and FormArrays, there must be an issue within it. However, I cannot seem to get it to work in order to nest and display a nested group.
Here are some approaches I have tried:
Angular FormArray: Cannot find control with path
Angular: Cannot find control with path: 'variable-> 0 -> id'
Angular 7 and form arrays error of cannot find a control with path
ERROR Error: Cannot find control with path
As a side-note, I'm not sure if this is even the best approach to implementing a nested reusable ponent, but I expect to research this further once I stop getting errors.
<form [formGroup]="form">
<div formArrayName="statement">
<div formArrayName="groups">
<div *ngFor="let group of form.get('statement.groups')['controls']; let i = index">
<fieldset>
<legend>Group {{ i + 1 }}:</legend>
<div [formGroupName]="i">
<span style="float: right;">
<button type="button" style="float: right; cursor: pointer; margin-left: 5px;" (click)="deleteGroup(i)">
delete group
</button>
<button type="button" style="cursor: pointer; margin-left: 5px;" (click)="addNestedGroup(i)">
add nested group
</button>
<button
type="button"
style="cursor: pointer; margin-left: 5px;"
(click)="addNewCondition(group.controls.conditions)"
>
add condition
</button>
</span>
<div formArrayName="conditions">
<div *ngFor="let condition of group.get('conditions')['controls']; let j = index">
<fieldset>
<legend>Condition {{ j + 1 }}</legend>
<div [formGroupName]="j">
<input style="vertical-align: middle;" type="text" formControlName="variable" />
<button
style="float: right; margin-bottom: 5px;"
(click)="deleteCondition(group.controls.conditions, j)"
>
delete condition
</button>
</div>
</fieldset>
</div>
</div>
<ng-container>
<div formArrayName="groups">
<div *ngFor="let num of group.get('groups').value; let idx = index">
<fieldset>
<legend>Group {{ 2 }}:</legend>
<span style="float: right;">
<button
type="button"
style="float: right; cursor: pointer; margin-left: 5px;"
(click)="deleteGroup(0)"
>
delete group
</button>
<button type="button" style="cursor: pointer; margin-left: 5px;" (click)="addNestedGroup(0)">
add nested group
</button>
<button
type="button"
style="cursor: pointer; margin-left: 5px;"
(click)="addNewCondition(num.conditions)"
>
add condition
</button>
</span>
<div formArrayName="conditions">
<div *ngFor="cond; of: group.controls; let k = index">
<fieldset>
<legend>Condition {{ k + 1 }}</legend>
<div [formGroupName]="k">
<input style="vertical-align: middle;" type="text" formControlName="variable" />
<button
style="float: right; margin-bottom: 5px;"
(click)="deleteCondition(group.controls.conditions, k)"
>
delete condition
</button>
</div>
</fieldset>
</div>
</div>
</fieldset>
</div>
</div>
</ng-container>
</div>
</fieldset>
</div>
</div>
</div>
</form>
I am building a nested, dynamic Form where the User has a group, and then can nest conditions within that group, or new additional group objects within a group
FormArray. Here is what the basic UI looks like. Note, not all the pieces are working, but for now I am trying to add a nested group. A nested group will work for the FormBuilder
, but it gives an error and does not show correctly on the UI. The error is: ERROR Error: Cannot find control with path: 'statement -> groups -> 0 -> groups -> conditions'
. Before going further, the StackBlitz can be found HERE
The form object looks like this:
{
"statement": {
"groups": [
{
"conjunctor": null,
"conditions": [
{
"variable": ""
}
],
"groups": []
}
]
}
}
Within the statement → groups → groups
the user is able to push an additional FormGroup
that will contain a "groups" object:
{
"conjunctor": null,
"conditions": [
{
"variable": ""
}
],
"groups": []
}
Long term, I expect to be able to push additional groups and further nest this Form, but for now, I am trying to get it to work on the UI. The HTML is as shown below and in this StackBlitz. I continue to get the error:
ERROR Error: Cannot find control with path: 'statement -> groups -> 0 -> groups -> conditions'
, and based off several S.O. examples, I recognize that this error is due to the way my HTML is nested and the FormGroups and FormArrays, there must be an issue within it. However, I cannot seem to get it to work in order to nest and display a nested group.
Here are some approaches I have tried:
Angular FormArray: Cannot find control with path
Angular: Cannot find control with path: 'variable-> 0 -> id'
Angular 7 and form arrays error of cannot find a control with path
ERROR Error: Cannot find control with path
As a side-note, I'm not sure if this is even the best approach to implementing a nested reusable ponent, but I expect to research this further once I stop getting errors.
<form [formGroup]="form">
<div formArrayName="statement">
<div formArrayName="groups">
<div *ngFor="let group of form.get('statement.groups')['controls']; let i = index">
<fieldset>
<legend>Group {{ i + 1 }}:</legend>
<div [formGroupName]="i">
<span style="float: right;">
<button type="button" style="float: right; cursor: pointer; margin-left: 5px;" (click)="deleteGroup(i)">
delete group
</button>
<button type="button" style="cursor: pointer; margin-left: 5px;" (click)="addNestedGroup(i)">
add nested group
</button>
<button
type="button"
style="cursor: pointer; margin-left: 5px;"
(click)="addNewCondition(group.controls.conditions)"
>
add condition
</button>
</span>
<div formArrayName="conditions">
<div *ngFor="let condition of group.get('conditions')['controls']; let j = index">
<fieldset>
<legend>Condition {{ j + 1 }}</legend>
<div [formGroupName]="j">
<input style="vertical-align: middle;" type="text" formControlName="variable" />
<button
style="float: right; margin-bottom: 5px;"
(click)="deleteCondition(group.controls.conditions, j)"
>
delete condition
</button>
</div>
</fieldset>
</div>
</div>
<ng-container>
<div formArrayName="groups">
<div *ngFor="let num of group.get('groups').value; let idx = index">
<fieldset>
<legend>Group {{ 2 }}:</legend>
<span style="float: right;">
<button
type="button"
style="float: right; cursor: pointer; margin-left: 5px;"
(click)="deleteGroup(0)"
>
delete group
</button>
<button type="button" style="cursor: pointer; margin-left: 5px;" (click)="addNestedGroup(0)">
add nested group
</button>
<button
type="button"
style="cursor: pointer; margin-left: 5px;"
(click)="addNewCondition(num.conditions)"
>
add condition
</button>
</span>
<div formArrayName="conditions">
<div *ngFor="cond; of: group.controls; let k = index">
<fieldset>
<legend>Condition {{ k + 1 }}</legend>
<div [formGroupName]="k">
<input style="vertical-align: middle;" type="text" formControlName="variable" />
<button
style="float: right; margin-bottom: 5px;"
(click)="deleteCondition(group.controls.conditions, k)"
>
delete condition
</button>
</div>
</fieldset>
</div>
</div>
</fieldset>
</div>
</div>
</ng-container>
</div>
</fieldset>
</div>
</div>
</div>
</form>
Share
Improve this question
edited Jun 13, 2020 at 23:06
Jeremy
asked Jun 12, 2020 at 20:50
JeremyJeremy
1,40822 silver badges43 bronze badges
2
- How many nested levels do you want to have? I can fix your current issue but I think you will ask for more plex example – yurzui Commented Jun 12, 2020 at 21:37
- Ultimately I'd like to have 3-4 levels deep (pared to 2 as shown right now): groups → groups. Long term I imagine having groups→groups→groups→groups. As far deeply nested as would ever need to be – Jeremy Commented Jun 12, 2020 at 22:21
1 Answer
Reset to default 8I've written a post at dev.to after writing this answer. Take a look.
You're dealing with a plex form. It's nested and it's recursive (as you have groups in groups). I suggest you split it into more ponents. By doing that, you'll make it easier to have a big picture of the whole form whenever you revisit it for any reason. And, as a very wele cherry-on-the-cake, you will avoid the deeply nested object paths you're using to access your controls (this can be overwhelming as you keep nesting dynamic forms the way you're doing).
What I'm trying to say is that the error is likely caused by some silly mistake in the deep object paths you use to get access to the form parts. When dealing with this kind of plex nested form, it usually isn't worth the effort to fix eventual issues related to wrong object paths: refactor it to get a more clean-structured ponent.
I strongly suggest you do the two things I'll describe below (also, take a look at this Stackblitz demo). What I did in that demo is a plete refactor of your form and I decided not to paste all the code here because it would be excessively long, hard to read, and you wouldn't be able to execute it anyway. So it would be pointless. Just go to the Stackblitz demo and try it there.
Your form has a very peculiar aspect: it's recursive. So everything is gonna be easier if we don't try to row the boat against its recursive nature stream.
I assure you I did nothing special in that code but just these two steps:
1 - Create a wrapper recursive form ponent:
Let's call it GroupFormComponent
. The one thing that's not so mon here is that, in the template of this ponent, you're gonna have... another GroupFormComponent
. Yes, you can bury an angular ponent inside itself recursively.
@Component({
selector: 'group-form',
template: `
...
<!-- here you nest another instance of this ponent -->
<group-form *ngIf="nestCondition"></group-form>
...
`,
})
export class GroupFormComponent {...}
The above snippet helps to illustrate what I'm suggesting you do (this kind of structure shows how far you can go with angular ponent-based nature => it's powerful, isn't it?).
2 - Split your form into more controls
You can (and should) group some parts of your form into other ponents, to make it easier to be understood as a whole. Without any great mental effort, we can identify three ponents:
The main form, containing the overall form
A bar of action buttons
A condition ponent
When you assemble all these parts together, you'll get:
<main-form>
<action-buttons></action-buttons>
<condition></condition>
<condition></condition>
...
<condition></condition>
<!-- The recursive part -->
<main-form></main-form>
<main-form></main-form>
...
<main-form></main-form>
</main-form>
To make it even more simple, <condition>
and <main-form>
ponents must implement the ControlValueAccessor
interface in order to allow them to be used as FormControl
's in other forms.
With all these this in place, you'll have a robust, maintainable, and flexible form.
The animated gif below shows it working.