如何用 Angular 8 创建递归形式?

How to create a recursive form with Angular 8?

我需要创建一个包含多个嵌套项的动态表单。我找到了 this example

但我不确定它是否在进行深度递归,因为一旦我尝试添加更多级别的嵌套项 - ui 停止。

这是我尝试的默认 json 结构:

 {
    key: "common",
    title: "main fields",
    group: [
      {
        key: "createdAt",
        title: "Create Date",
        type: "date"
      },
             //   group:[{
      //     key: "foo",
      //     title: "Foo",
      //     type: "select",
      //   },
      //   {
      //     key: "goo",
      //     title: "Goo",
      //     type: "input",
      //   },
     
      // ]
    ]
  },

正如您在“普通”下看到的那样 - 我又添加了 2 个级别的组 - 第一个组工作正常 - 但具有键“foo”和“goo”的嵌套组正在工作。

我很确定问题出在模板/标记中

<form [formGroup]="filterForm" class="filter-form">
  <ng-template #recursiveList let-filterFields let-fromGroup="fromGroup">
    <ng-container *ngFor="let item of filterFields">
      <ng-container *ngIf="item.group; else default;">
          // in this area i'm not sure it's iterate over deeper nesting...
          <p>{{item.key}} </p>
          
          <div  [formGroupName]="item.key">
              <ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: 
          item.group, fromGroup: {name: item.key}, isChild:true }"></ng-container>
          </div>
      </ng-container>
      <ng-template #default>       
        
              <div class="form-group" [formGroupName]="fromGroup.name">
                  <input [type]="item.type" [formControlName]="item.key" 
               [placeholder]="item.title" [name]="item.key" />
              </div>
         
      </ng-template>
  </ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: filterFields 
 }">. 

据我了解,您提供的示例中存在两个问题:

  • 数据结构。
  • 模板。

数据结构

这些是我从您的示例中了解到的接口:

interface Base {
  key: string;
  title: string;
}

interface Field extends Base {
  type: 'select' | 'input' | 'date' | ...
}

interface Group extends Base {
  group: Array<Field | Group>
}

所以您提供的 JSON 示例应该如下所示:

{
  "key": "common",
  "title": "main fields",
  "group": [
    {
      "key": "createdAt",
      "title": "Create Date",
      "type": "date"
    },
    {
      "key": "test",
      "title": "Test"
      "group": [
        {
          "key": "foo",
          "title": "Foo",
          "type": "select"
        },
        {
          "key": "goo",
          "title": "Goo",
          "type": "input"
        }
      ]
    }
  ]
}

模板

让我们来看一个非常简化的形式:

<form [formGroup]="filterForm">
  
  <ng-container formGroupName="common">
    <ng-container *ngTemplateOutlet="control; 
      context:{ controlName: 'foo', group: 'test' }">
    </ng-container>
  </ng-container>

  <ng-template #control let-group="group" let-controlName="controlName">
    <div class="col-md-3">
      <div class="form-group" [formGroupName]="group">
        <input type="input" [formControlName]="controlName" />
      </div>
    </div>
  </ng-template>

</form>

代码无法运行,为什么?将 ng-template 视为一个函数。如果你想让它知道formGroupName="common",它需要在那个范围内声明。重要的是声明上下文而不是调用上下文,就像常规函数一样。

这是上面例子的工作版本:

<form [formGroup]="filterForm">

  <ng-container formGroupName="common">
    <ng-container *ngTemplateOutlet="control; 
      context:{ controlName: 'foo', group: 'test' }">
    </ng-container>

    <ng-template #control let-group="group" let-controlName="controlName">
      <div class="col-md-3">
        <div class="form-group" [formGroupName]="group">
          <input type="input" [formControlName]="controlName" />
        </div>
      </div>
    </ng-template>
    
  </ng-container>

</form>

当您嵌套并且需要使用递归时,事情会变得更加棘手。
这就是为什么我认为在这种情况下使用 formGroupNameformControlName 指令的方法会使事情变得比实际情况更复杂。

我建议通过提供正确的路径将表单控件直接传递到输入中。

Here 是基于您的原始示例的想法的工作示例。