最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular Deeply Nested Reactive Form: Cannot find control with path on nested FormArray - Stack Overflow

programmeradmin0浏览0评论

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
Add a ment  | 

1 Answer 1

Reset to default 8

I'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.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论