动态形式的 FormArray angular
FormArray in dynamic form angular
我实现了一个 stackblitz,您可以在其中使用一些配置创建动态表单。在你使用 FormArray
之前一切正常。什么想法?基本上,您的配置文件中可以有许多不同类型的字段。例如 checkbox
、text
、number
select
等。在某些情况下,你应该有一个更复杂的结构,所以你应该有一个 FormArray
。在这个 stackblitz 中,我试图代表正在发生的事情
所以,当我找到一个 array
类型时,我将创建一个 FormArray 而不是一个简单的 FormControl
这是我的字段列表
public fields: any[] = [
{
type: "text",
name: "firstName",
label: "First Name",
required: true
},
{
type: "text",
name: "lastName",
label: "Last Name",
required: true
},
{
type: "text",
name: "email",
label: "Email",
required: true
},
{
type: "text",
name: "description",
label: "Description",
required: true,
multiLine: true
},
{
type: "dropdown",
name: "country",
label: "Country",
value: "in",
required: true,
options: [{ value: "in", label: "India" }, { value: "us", label: "USA" }]
},
{
type: "array",
name: "users",
label: "Users",
structure: "users.json"
}
];
这是我的动态表单生成器组件
export class DynamicFormBuilderComponent implements OnInit {
@Output() onSubmit = new EventEmitter();
@Input() fields: any[] = [];
form: FormGroup;
allDatas: any;
constructor() {}
ngOnInit() {
let fieldsCtrls = {};
this.allDatas = getData();
for (let f of this.fields) {
if (f.type != "array") {
fieldsCtrls[f.name] = new FormControl(
this.allDatas[f.name] || "",
Validators.required
);
} else {
fieldsCtrls[f.name] = new FormArray([
new FormControl(this.allDatas[f.name] || "", Validators.required)
]);
}
}
this.form = new FormGroup(fieldsCtrls);
}
}
如您所见,我从 allDatas 中设置了一个值(如果存在),并使用 FormControls 和 FormArray 创建了 FormGroup。但是当我生成 arraybox
时出现错误。这是显示 FormArray 控件的数组框组件:
import { HttpClient } from "@angular/common/http";
import { Component, Input, OnInit } from "@angular/core";
import { FormGroup } from "@angular/forms";
import { ConfigService } from "../../config.service";
@Component({
selector: "arraybox",
template: `
<div [formGroup]="form">
<div formArrayName="{{ field.name }}">
<div
*ngFor="let obj of arrayFileds; index as idx"
[formGroupName]="idx"
>
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ idx }}"
/>
</div>
</div>
</div>
`
})
export class ArrayBoxComponent implements OnInit {
@Input() field: any = {};
@Input() form: FormGroup;
arrayFileds: any = [];
get isValid() {
return this.form.controls[this.field.name].valid;
}
get isDirty() {
return this.form.controls[this.field.name].dirty;
}
constructor(private config: ConfigService) {}
ngOnInit(): void {
this.arrayFileds = this.config.getData(this.field.structure);
console.log(this.arrayFileds);
}
}
getData() returns 该 FormArray 的结构。在这种情况下
[{
"label": "Username",
"name": "userName",
"type": "text"
}, {
"label": "User email",
"name": "userEmail",
"type": "text"
}]
控制台中的错误是:
ERROR
Error: Cannot find control with path: 'users -> 0 -> 0'
ERROR
Error: Cannot find control with path: 'users -> 1'
我真的不知道这里出了什么问题。我如何使用 FormArray 在此表单中显示字段及其值?
注意。如果我使用 http 解析 json,stackblitz 会给我一个错误,所以现在我直接使用 json 的导入。基本上,这个例子中的 this.field.structure
没有被使用(但它应该被使用)。
检查此 stackblitz
示例:-
https://stackblitz.com/edit/angular-dynamic-form-creation
希望对你有用!
嗯,干得漂亮,但您需要更改一些东西才能实现您想要的。
- 您需要更改初始化表单数组的方式。您在
FormArray
中命名了控件,因此您需要将 FormGroup
推入 FormArray
:
for (let f of this.fields) {
if (f.type != "array") {
fieldsCtrls[f.name] = new FormControl(
this.allDatas[f.name] || "",
Validators.required
);
} else { // changed part
fieldsCtrls[f.name] = new FormArray([
...this.allDatas[f.name].map(
r =>
new FormGroup(
Object.entries(r).reduce((acc, [k, v]) => {
acc[k] = new FormControl(v || "", Validators.required);
return acc;
}, {})
)
)
]);
}
上面的代码基本上生成 FormGroup
对象键为 FormControls
- 您需要更改
ArrayBox
组件模板,如下所示:
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ obj.name }}"
/>
您有 formControlName="{{ idx }}"
,这将导致 Angular 寻找名为 0 1
的 FormControl
,这就是您收到 Error: Cannot find control with path: 'users -> 0 -> 0'
错误的原因之一。另一个原因是您直接添加 FormControl
s 而不是 FormGroup
s。正如您的 ArrayBox
模板所述 FormArray
有一个数组 FormGroup
因为@End.Game 注意到数组部分仍有一些部分需要修复。问题的根本原因在于ArrayBox
模板。您正在迭代您的配置对象以呈现您的表单。但由于它是 FormArray
,您还需要为 FormArray
的计数重复该模板。所以你需要另一个 *ngFor
来实现:
<div [formGroup]="form">
<div formArrayName="{{ field.name }}">
<div *ngFor="let frm of formArray.controls; index as idx">
<div formGroupName="{{ idx }}">
<div *ngFor="let obj of arrayFileds;">
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ obj.name }}"
/>
</div>
<hr />
</div>
</div>
</div>
</div>
另请注意,已将 idx
变量移至父循环。
我实现了一个 stackblitz,您可以在其中使用一些配置创建动态表单。在你使用 FormArray
之前一切正常。什么想法?基本上,您的配置文件中可以有许多不同类型的字段。例如 checkbox
、text
、number
select
等。在某些情况下,你应该有一个更复杂的结构,所以你应该有一个 FormArray
。在这个 stackblitz 中,我试图代表正在发生的事情
所以,当我找到一个 array
类型时,我将创建一个 FormArray 而不是一个简单的 FormControl
这是我的字段列表
public fields: any[] = [
{
type: "text",
name: "firstName",
label: "First Name",
required: true
},
{
type: "text",
name: "lastName",
label: "Last Name",
required: true
},
{
type: "text",
name: "email",
label: "Email",
required: true
},
{
type: "text",
name: "description",
label: "Description",
required: true,
multiLine: true
},
{
type: "dropdown",
name: "country",
label: "Country",
value: "in",
required: true,
options: [{ value: "in", label: "India" }, { value: "us", label: "USA" }]
},
{
type: "array",
name: "users",
label: "Users",
structure: "users.json"
}
];
这是我的动态表单生成器组件
export class DynamicFormBuilderComponent implements OnInit {
@Output() onSubmit = new EventEmitter();
@Input() fields: any[] = [];
form: FormGroup;
allDatas: any;
constructor() {}
ngOnInit() {
let fieldsCtrls = {};
this.allDatas = getData();
for (let f of this.fields) {
if (f.type != "array") {
fieldsCtrls[f.name] = new FormControl(
this.allDatas[f.name] || "",
Validators.required
);
} else {
fieldsCtrls[f.name] = new FormArray([
new FormControl(this.allDatas[f.name] || "", Validators.required)
]);
}
}
this.form = new FormGroup(fieldsCtrls);
}
}
如您所见,我从 allDatas 中设置了一个值(如果存在),并使用 FormControls 和 FormArray 创建了 FormGroup。但是当我生成 arraybox
时出现错误。这是显示 FormArray 控件的数组框组件:
import { HttpClient } from "@angular/common/http";
import { Component, Input, OnInit } from "@angular/core";
import { FormGroup } from "@angular/forms";
import { ConfigService } from "../../config.service";
@Component({
selector: "arraybox",
template: `
<div [formGroup]="form">
<div formArrayName="{{ field.name }}">
<div
*ngFor="let obj of arrayFileds; index as idx"
[formGroupName]="idx"
>
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ idx }}"
/>
</div>
</div>
</div>
`
})
export class ArrayBoxComponent implements OnInit {
@Input() field: any = {};
@Input() form: FormGroup;
arrayFileds: any = [];
get isValid() {
return this.form.controls[this.field.name].valid;
}
get isDirty() {
return this.form.controls[this.field.name].dirty;
}
constructor(private config: ConfigService) {}
ngOnInit(): void {
this.arrayFileds = this.config.getData(this.field.structure);
console.log(this.arrayFileds);
}
}
getData() returns 该 FormArray 的结构。在这种情况下
[{
"label": "Username",
"name": "userName",
"type": "text"
}, {
"label": "User email",
"name": "userEmail",
"type": "text"
}]
控制台中的错误是:
ERROR
Error: Cannot find control with path: 'users -> 0 -> 0'
ERROR
Error: Cannot find control with path: 'users -> 1'
我真的不知道这里出了什么问题。我如何使用 FormArray 在此表单中显示字段及其值?
注意。如果我使用 http 解析 json,stackblitz 会给我一个错误,所以现在我直接使用 json 的导入。基本上,这个例子中的 this.field.structure
没有被使用(但它应该被使用)。
检查此 stackblitz
示例:-
https://stackblitz.com/edit/angular-dynamic-form-creation
希望对你有用!
嗯,干得漂亮,但您需要更改一些东西才能实现您想要的。
- 您需要更改初始化表单数组的方式。您在
FormArray
中命名了控件,因此您需要将FormGroup
推入FormArray
:
for (let f of this.fields) {
if (f.type != "array") {
fieldsCtrls[f.name] = new FormControl(
this.allDatas[f.name] || "",
Validators.required
);
} else { // changed part
fieldsCtrls[f.name] = new FormArray([
...this.allDatas[f.name].map(
r =>
new FormGroup(
Object.entries(r).reduce((acc, [k, v]) => {
acc[k] = new FormControl(v || "", Validators.required);
return acc;
}, {})
)
)
]);
}
上面的代码基本上生成 FormGroup
对象键为 FormControls
- 您需要更改
ArrayBox
组件模板,如下所示:
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ obj.name }}"
/>
您有 formControlName="{{ idx }}"
,这将导致 Angular 寻找名为 0 1
的 FormControl
,这就是您收到 Error: Cannot find control with path: 'users -> 0 -> 0'
错误的原因之一。另一个原因是您直接添加 FormControl
s 而不是 FormGroup
s。正如您的 ArrayBox
模板所述 FormArray
有一个数组 FormGroup
因为@End.Game 注意到数组部分仍有一些部分需要修复。问题的根本原因在于ArrayBox
模板。您正在迭代您的配置对象以呈现您的表单。但由于它是 FormArray
,您还需要为 FormArray
的计数重复该模板。所以你需要另一个 *ngFor
来实现:
<div [formGroup]="form">
<div formArrayName="{{ field.name }}">
<div *ngFor="let frm of formArray.controls; index as idx">
<div formGroupName="{{ idx }}">
<div *ngFor="let obj of arrayFileds;">
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ obj.name }}"
/>
</div>
<hr />
</div>
</div>
</div>
</div>
另请注意,已将 idx
变量移至父循环。