Javascript 角度嵌套的反应式窗体:在嵌套窗体上找不到路径为的控件

Javascript 角度嵌套的反应式窗体:在嵌套窗体上找不到路径为的控件,javascript,html,angular,Javascript,Html,Angular,我正在构建一个嵌套的动态表单,其中用户有一个组,然后可以在该组中嵌套条件,或者在组FormArray中嵌套新的附加组对象。下面是基本UI的外观。注意,并不是所有的部分都正常工作,但现在我正在尝试添加一个嵌套组。嵌套组将适用于FormBuilder,但它会给出一个错误,并且不会在UI上正确显示。错误是:错误:找不到路径为“语句->组->0->组->条件”的控件。在继续之前,可以找到StackBlitz 表单对象如下所示: { "statement": { "groups": [

我正在构建一个嵌套的动态表单,其中用户有一个组,然后可以在该组中嵌套条件,或者在组FormArray中嵌套新的附加组对象。下面是基本UI的外观。注意,并不是所有的部分都正常工作,但现在我正在尝试添加一个嵌套组。嵌套组将适用于FormBuilder,但它会给出一个错误,并且不会在UI上正确显示。错误是:错误:找不到路径为“语句->组->0->组->条件”的控件。在继续之前,可以找到StackBlitz

表单对象如下所示:

{
  "statement": {
    "groups": [
      {
        "conjunctor": null,
        "conditions": [
          {
            "variable": ""
          }
        ],
        "groups": []
      }
    ]
  }
}
在声明中→ 组→ 组用户可以推送包含组对象的其他FormGroup:

 {
 "conjunctor": null,
   "conditions": [
     {
       "variable": ""
     }
    ],
   "groups": []
 }
从长远来看,我希望能够推动其他组并进一步嵌套此表单,但目前,我正在尝试让它在UI上工作。HTML如下所示,如下所示。我继续得到错误信息: 错误:找不到路径为“statement->groups->0->groups->conditions”的控件,根据几个S.O.示例,我认识到此错误是由于HTML的嵌套方式以及FormGroups和FormArray造成的,其中一定有问题。但是,我似乎无法让它工作,以便嵌套和显示嵌套组。 以下是我尝试过的一些方法:

顺便说一句,我不确定这是否是实现嵌套可重用组件的最佳方法,但我希望在停止出错后进一步研究这一点

<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>
在写了这个答案之后,我在上写了一篇文章。看一看

您正在处理一个复杂的表单。它是嵌套的,并且是递归的,因为在组中有组。我建议你把它分成更多的部分。通过这样做,无论何时出于任何原因重新访问整个表单,您都可以更轻松地了解整个表单。而且,作为蛋糕上非常受欢迎的樱桃,您将避免用于访问控件的深度嵌套对象路径。如果您继续以这种方式嵌套动态表单,那么这可能会让人难以忍受

我想说的是,这个错误很可能是由于您用来访问表单部件的深层对象路径中的一些愚蠢错误造成的。在处理这种复杂的嵌套表单时,通常不值得花费精力来修复与错误对象路径相关的最终问题:重构它以获得更干净的结构化组件

我强烈建议你做我将在下面描述的两件事,看看这个。我在那个演示中所做的是对您的表单进行完整的重构,我决定不将所有代码粘贴到这里,因为它太长,很难阅读,而且您无论如何也无法执行它。所以这是毫无意义的。只要去商店试试就行了

您的表单有一个非常特殊的方面:它是递归的。所以,如果我们不试着让船逆流而上,一切都会变得更容易

我向您保证,我在代码中没有做什么特别的事情,只做了以下两个步骤:

1-创建包装器递归表单组件: 我们称之为GroupFormComponent。这里不常见的一件事是,在这个组件的模板中,您将有。。。另一个GroupFormComponent。是的,你可以递归地将一个角度分量埋在它的内部

@组成部分{ 选择器:'组形式', 模板:` ... ... `, } 导出类GroupFormComponent{…} 上面的代码片段有助于说明我建议您做的事情。这种结构显示了基于angular组件的nature=>功能强大,不是吗

2-将窗体拆分为多个控件 您可以而且应该将表单的某些部分分组到其他组件中,以使其更易于作为一个整体来理解。我们不需要花很大的脑力就能识别出三个组成部分:

主窗体,包含整个窗体

一列动作按钮

条件组件

当您将所有这些零件组装在一起时,您将得到:

我在写完这个答案后在上写了一篇帖子。看一看

您正在处理一个复杂的表单。它是嵌套的,并且是递归的,因为在组中有组。我建议你把它分成更多的部分。通过这样做,无论何时出于任何原因重新访问整个表单,您都可以更轻松地了解整个表单。而且,作为蛋糕上非常受欢迎的樱桃,您将避免用于访问控件的深度嵌套对象路径。如果您继续以这种方式嵌套动态表单,那么这可能会让人难以忍受

我想说的是,这个错误很可能是由于您用来访问表单部件的深层对象路径中的一些愚蠢错误造成的。在处理这种复杂的嵌套表单时,通常不值得花费精力来修复与错误的对象路径相关的最终问题:重构它以获得更干净的结构 重要的

我强烈建议你做我将在下面描述的两件事,看看这个。我在那个演示中所做的是对您的表单进行完整的重构,我决定不将所有代码粘贴到这里,因为它太长,很难阅读,而且您无论如何也无法执行它。所以这是毫无意义的。只要去商店试试就行了

您的表单有一个非常特殊的方面:它是递归的。所以,如果我们不试着让船逆流而上,一切都会变得更容易

我向您保证,我在代码中没有做什么特别的事情,只做了以下两个步骤:

1-创建包装器递归表单组件: 我们称之为GroupFormComponent。这里不常见的一件事是,在这个组件的模板中,您将有。。。另一个GroupFormComponent。是的,你可以递归地将一个角度分量埋在它的内部

@组成部分{ 选择器:'组形式', 模板:` ... ... `, } 导出类GroupFormComponent{…} 上面的代码片段有助于说明我建议您做的事情。这种结构显示了基于angular组件的nature=>功能强大,不是吗

2-将窗体拆分为多个控件 您可以而且应该将表单的某些部分分组到其他组件中,以使其更易于作为一个整体来理解。我们不需要花很大的脑力就能识别出三个组成部分:

主窗体,包含整个窗体

一列动作按钮

条件组件

当您将所有这些零件组装在一起时,您将得到:


您希望有多少嵌套级别?我可以解决您当前的问题,但我认为您会要求更复杂的示例最终我希望有3-4个级别,而不是现在显示的2个级别:组→ 组。从长远来看,我想象有一群人→组→组→组。尽可能深的嵌套,您希望有多少嵌套级别?我可以解决您当前的问题,但我认为您会要求更复杂的示例最终我希望有3-4个级别,而不是现在显示的2个级别:组→ 组。从长远来看,我想象有一群人→组→组→组。贝蒂斯需要的最深的巢穴是难以置信的。这是一个多么奇妙的方法。非常感谢StackBlitz,以及对它的解释。一切都很好,我很高兴。我喜欢这样做。事实上,我想我会写一篇关于它的博客文章。如果你写了,我很乐意阅读并分享。如果你还记得,请随意分享这些评论。感谢againarready发布:尝试在表单组件中实现验证器接口。我已经在我的回答中提出了stackblitz,并在其中一个组件中添加了一个组件。这是难以置信的。这是一个多么奇妙的方法。非常感谢StackBlitz,以及对它的解释。一切都很好,我很高兴。我喜欢这样做。事实上,我想我会写一篇关于它的博客文章。如果你写了,我很乐意阅读并分享。如果你还记得,请随意分享这些评论。感谢againarready发布:尝试在表单组件中实现验证器接口。我在回答中提出了stackblitz,并在其中一个组件中添加了一个。