如何用 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>
当您嵌套并且需要使用递归时,事情会变得更加棘手。
这就是为什么我认为在这种情况下使用 formGroupName
和 formControlName
指令的方法会使事情变得比实际情况更复杂。
我建议通过提供正确的路径将表单控件直接传递到输入中。
Here 是基于您的原始示例的想法的工作示例。
我需要创建一个包含多个嵌套项的动态表单。我找到了 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>
当您嵌套并且需要使用递归时,事情会变得更加棘手。
这就是为什么我认为在这种情况下使用 formGroupName
和 formControlName
指令的方法会使事情变得比实际情况更复杂。
我建议通过提供正确的路径将表单控件直接传递到输入中。
Here 是基于您的原始示例的想法的工作示例。