相对角形式>用户自定义表格错误 [英] Angular Reative Forms > User customized forms error
问题描述
我正在尝试在我的角度应用程序中实现一项功能,使用户能够创建自己的表单(选择字段/验证/等).一旦他们创建了表单,我将其JSON结构存储在数据库中以备后用:
export interface FormModel {
id: string;
name: string;
type: 'group' | 'control';
children: FormModel[];
isArray: boolean;
}
样本结构是这样的:
[{
id: 'users',
name: 'Users',
type: 'group',
isArray: true,
children: [
{
id: "user",
name: "User",
type: 'group',
isArray: false,
children: [
{
id: "name",
name: "Name",
type: 'control',
children: null,
isArray: false
},
{
id: "age",
name: "Age",
type: 'control',
children: null,
isArray: false
}
]
},
{
id: "user",
name: "User",
type: 'group',
isArray: false,
children: [
{
id: "name",
name: "Name",
type: 'control',
children: null,
isArray: false,
},
{
id: "age",
name: "Age",
type: 'control',
children: null,
isArray: false
}
]
}
]
}, {
id: 'comments',
name: 'Comments',
type: 'control',
children: null,
isArray: false
}
];
基于从数据库加载的JSON创建响应式表单后,由于具有递归,我很难创建相应的html.
经过多次尝试,我设法到达以下位置,在该处生成类似于我所需要的HTML:
<div formGroupName="users">
<div formArrayName="0">
<div formGroupName="user">
<input type="text" formControlName="name">
<input type="text" formControlName="age">
</div>
</div>
<div formArrayName="0">
<div formGroupName="user">
<input type="text" formControlName="name">
<input type="text" formControlName="age">
</div>
</div>
</div>
我使用的模板如下:
<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
<div formGroupName="variables">
<ng-template #recursiveList let-controls let-prefix="prefix">
<ng-container *ngFor="let item of controls; let i = index;">
<input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">
<div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
<div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: formFields, prefix: '' }">
</ng-container>
<ng-template #recursiveArray let-controls let-prefix="prefix">
<ng-container *ngFor="let item of controls; let i = index;">
<div [formGroupName]="i">
<input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">
<div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
<div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
</div>
</ng-container>
</ng-template>
</div>
</form>
在我看来这是正确的,但我不断出错:
ERROR
Error: Cannot find control with path: 'variables -> 0'
ERROR
Error: Cannot find control with path: 'variables -> 0 -> user'
我用示例创建了一个堆栈闪电: https://stackblitz.com/edit/angular-mtbien
你们能帮助我找出问题所在吗?我已经为此工作了2天,但没有成功:(
谢谢!
正如Ersenkoening所说,要使用FormsGroup和Form Arrays,可以直接使用控件".使用FormArrayName,Form Group等可能真是令人头疼.
看到Ersenkoening的form-array.component.html可以像这样更简单地编码
<div [formGroup]="formArray">
<ng-container *ngFor="let child of formArray.controls; let i = index">
<app-form-group [formGroup]="child"></app-form-group>
</ng-container>
</div>
是,是管理FormArray的另一种方法,但请记住,formArray只是一个特别的" FormGroup.
更新 有了这个想法,我们将更加深入,在html中仅使用[formControl],因此,我们需要将"control"作为变量传递.参见 stackblitz >
表单提交的视图就像
<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
<div *ngFor="let item of formFields;let i=index">
{{item.id}}
<ng-container *ngTemplateOutlet="recursiveList; context:{
$implicit: formFields[i],
<!--see how pass the control of myForm--->
control:myForm.get(item.id) }">
</ng-container>
</div>
<ng-template #recursiveList let-item let-control="control">
<div *ngIf="!item.children">
{{item.id}}<input [formControl]="control">
</div>
<div *ngIf="item.children">
<div *ngIf="!item.isArray">
<div *ngFor="let children of item.children">
<ng-container *ngTemplateOutlet="recursiveList; context:{
$implicit:children,
<!--see how pass the control of a formGroup--->
control:control.get(children.id)}">
</ng-container>
</div>
</div>
<div *ngIf="item.isArray">
<div *ngFor="let children of item.children;let i=index">
<ng-container *ngTemplateOutlet="recursiveList; context:{
$implicit:children,
<!--see how pass the control of a formArray--->
control:control.at(i)}">
</ng-container>
</div>
</div>
</div>
</ng-template>
</form>
<pre>
{{myForm?.value|json}}
</pre>
更新2 简化了表单的创建,请参见 I am trying to implement a feature on my angular app where the users will be able to create their own forms (selecting fields/validation/etc). Once they create the form, I'll store it's JSON structure on the DB for use later: A sample structure is this: After creating the reactive form based on the JSON loaded from the DB, I'm having difficulties creating the respective html since it has recursion. After many attempts, I managed to get to the following, where it generates a HTML similar to what I understand should be needed: The template I've used is the following: It seems to me that it is right, but I keep getting errors: I have created a stackblitz with the sample:
https://stackblitz.com/edit/angular-mtbien Could you guys help me identify the problem? I've been working on this for 2 days without any success :( Thanks! As Ersenkoening say, for work with FormsGroup and Form Arrays you can use the "controls", directly. Use FormArrayName, Form Group, etc, can be a real headache. See that the form-array.component.html of Ersenkoening can be coded more simple like Yes is a different way to mannage a FormArray but remember that a formArray it's only a "especial" FormGroup. Update
With this idea, we are going to go more deeper, in the html only use [formControl], so, we need pass the "control" as a variable. See stackblitz The form-filed-view is like Update 2 simplify the creation of the form, see stackblitz
这篇关于相对角形式>用户自定义表格错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!export interface FormModel {
id: string;
name: string;
type: 'group' | 'control';
children: FormModel[];
isArray: boolean;
}
[{
id: 'users',
name: 'Users',
type: 'group',
isArray: true,
children: [
{
id: "user",
name: "User",
type: 'group',
isArray: false,
children: [
{
id: "name",
name: "Name",
type: 'control',
children: null,
isArray: false
},
{
id: "age",
name: "Age",
type: 'control',
children: null,
isArray: false
}
]
},
{
id: "user",
name: "User",
type: 'group',
isArray: false,
children: [
{
id: "name",
name: "Name",
type: 'control',
children: null,
isArray: false,
},
{
id: "age",
name: "Age",
type: 'control',
children: null,
isArray: false
}
]
}
]
}, {
id: 'comments',
name: 'Comments',
type: 'control',
children: null,
isArray: false
}
];
<div formGroupName="users">
<div formArrayName="0">
<div formGroupName="user">
<input type="text" formControlName="name">
<input type="text" formControlName="age">
</div>
</div>
<div formArrayName="0">
<div formGroupName="user">
<input type="text" formControlName="name">
<input type="text" formControlName="age">
</div>
</div>
</div>
<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
<div formGroupName="variables">
<ng-template #recursiveList let-controls let-prefix="prefix">
<ng-container *ngFor="let item of controls; let i = index;">
<input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">
<div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
<div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
</ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: formFields, prefix: '' }">
</ng-container>
<ng-template #recursiveArray let-controls let-prefix="prefix">
<ng-container *ngFor="let item of controls; let i = index;">
<div [formGroupName]="i">
<input type="text" [formControlName]="item.id" *ngIf="(item?.children?.length > 0) == false">
<div *ngIf="item?.children?.length > 0 && item.isArray" [formArrayName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveArray; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
<div *ngIf="item?.children?.length > 0 && !item.isArray" [formGroupName]="item.id">
<ng-container
*ngTemplateOutlet="recursiveList; context:{ $implicit: item.children, prefix: item.isArray }">
</ng-container>
</div>
</div>
</ng-container>
</ng-template>
</div>
</form>
ERROR
Error: Cannot find control with path: 'variables -> 0'
ERROR
Error: Cannot find control with path: 'variables -> 0 -> user'
<div [formGroup]="formArray">
<ng-container *ngFor="let child of formArray.controls; let i = index">
<app-form-group [formGroup]="child"></app-form-group>
</ng-container>
</div>
<form name="myForm" [formGroup]="myForm" fxLayout="column" fxFlex>
<div *ngFor="let item of formFields;let i=index">
{{item.id}}
<ng-container *ngTemplateOutlet="recursiveList; context:{
$implicit: formFields[i],
<!--see how pass the control of myForm--->
control:myForm.get(item.id) }">
</ng-container>
</div>
<ng-template #recursiveList let-item let-control="control">
<div *ngIf="!item.children">
{{item.id}}<input [formControl]="control">
</div>
<div *ngIf="item.children">
<div *ngIf="!item.isArray">
<div *ngFor="let children of item.children">
<ng-container *ngTemplateOutlet="recursiveList; context:{
$implicit:children,
<!--see how pass the control of a formGroup--->
control:control.get(children.id)}">
</ng-container>
</div>
</div>
<div *ngIf="item.isArray">
<div *ngFor="let children of item.children;let i=index">
<ng-container *ngTemplateOutlet="recursiveList; context:{
$implicit:children,
<!--see how pass the control of a formArray--->
control:control.at(i)}">
</ng-container>
</div>
</div>
</div>
</ng-template>
</form>
<pre>
{{myForm?.value|json}}
</pre>
ngOnInit() {
let group = new FormGroup({});
this.formFields.forEach(element => {
let formItem = this.createFormGroup(element);
group.addControl(element.id, formItem);
});
this.myForm = group;
}
private createFormGroup(formItem: FormModel) {
if (formItem.type=="group")
{
if (formItem.isArray && formItem.children.length<formItem.minQtd)
{
const add={...formItem.children[0]}
//here we can "clean" the value
while (formItem.children.length<formItem.minQtd)
formItem.children.push(add)
}
let group:FormGroup=new FormGroup({});
let controls:any[]=[]
formItem.children.forEach(element=>{
let item=this.createFormGroup(element);
if (formItem.isArray)
controls.push(item);
else
group.addControl(element.id, item);
})
if (!formItem.isArray)
return group;
return new FormArray(controls)
}
if (formItem.type=="control")
return new FormControl();
}