带有formarray的角度动态表单生成器 [英] Dynamic form Builder in angular with formarray
问题描述
我已经在我的项目中实现了动态表单构建器,它运行良好.但是,现在我需要在动态表单构建器中添加一个表单.这就像表单中的表单.所以,我在我的 ts 中创建了一个 formarray 并且它正确地出现了.
我收到类似的错误
错误错误:找不到名称为891713"的控件
这是我实施的https://angular.io/guide/dynamic-form
我使用的代码示例是 https://stackblitz.com/edit/angular-动态表单构建器
所以我在文件夹atoms"中创建了一个名为 formControl 的组件,如果控件是 fromControl,我将再次调用字段控件.
FormControl 组件
FormControl 组件 Ts
<预><代码>@Input() 字段:any = {};@Input() 表单:FormGroup;@Output() sendDataToParent = new EventEmitter();//表单:表单组;项目长度:任意;项目:任何;元素数据 = {}行 = [];isDisabled : boolean = false;孩子:FormArray;formNewStatus : boolean = true;//孩子:任何;ngOnInit() {this.items = this.field.items;this.itemLength = this.items.lengththis.items.forEach(元素 => {const elemId =element.id;this.elemData[elemId] = ''});console.log("来自控制表单======>",this.form)this.row.push(this.elemData);}添加表(){const frmArr = this.form.get('childs') as FormArray;this.sendDataToParent.emit(添加");this.row.push(this.elemData)console.log("this.row===>",this.row)}删除行(x){this.row.splice(x, 1);}删除所有行(){console.log(来到这里")this.row = [];}在父组件中,我已经添加了formarray的代码来添加更多
onFormControlValueChange(parentField:any){if(parentField === '添加'){this.childs = this.form.get('childs') as FormArray;this.childs.push(this.fb.group(this.createItem()));}console.log("this form====>", this.form)}
谁能帮我解决这个问题?提前致谢!:)
解决方案噗,这是一个庞大而复杂的改进代码以允许 fromGroups 和 formArray,但我们将尝试.
首先我们要更改一些 DynamicFormBuilderComponent
我们将创建一个函数 getForm 来递归创建 formGroup
getForm(group: FormGroup, fields: any[]) {for(让 f 个字段){开关(f.type){案例组":group.addControl(f.name, new FormGroup({}));this.getForm(group.get(f.name) as FormGroup, f.children);休息;案例复选框":group.addControl(f.name, new FormGroup({}));const groupOption = group.get(f.name) as FormGroup;for(让选择 f.options){groupOption.addControl(opt.key, new FormControl(opt.value));}休息;案例数组":group.addControl(f.name, new FormArray([]));const array = group.get(f.name) as FormArray;如果(f.value){f.value.forEach(x=>array.push(this.addGroupArray(f.children)))array.patchValue(f.value)}休息;默认:group.addControl(f.名称,new FormControl(f.value || "", Validators.required));休息;}}
在我们创建对象 fields
并最终将对象字段添加到 formGroup 之前,请参阅.使用这个函数,我们直接创建了一个空的 formGroup,我们使用 group.addControl
添加 FormControls 或 FormGroup 或 FormArray.这允许我们在必要时调用递归函数.
所以,在 ngOnInit 中我们制作
ngOnInit() {this.getForm(this.form, this.fields);}
看到我们可以使用模板变量从父级访问表单
{{dynamic.form?.value|json}}
以及我如何确定表单具有精确"模型 - 在我们有一个带有 {"fields:"exactModel"} 的对象之前-
我们需要一个辅助函数来创建一个 FormArray 的 formGroup
addGroupArray(fields:any[]){const group:FormGroup=new FormGroup({})fields.forEach(x=>{group.addControl(x.name,new FormControl(null,x.required?Validators.required:null))})返回组}
我们的 FieldBuilderComponent 应该考虑新的两种类型的字段:array";和组".我去把它放在一个字段集中
<!--在 case group 我们重复使用 field-builder作为getFormGroup"的形式并作为字段field.children"--><fieldset *ngSwitchCase="'group'"><legend>{{field.name}}</legend><field-builder *ngFor="let item of field.children";[form]="getFormGroup(field.name)";[字段]=项目"></field-builder></fieldset><!--在数组的情况下,我们创建一个表并使用该函数getFormArray"--><fieldset *ngSwitchCase="'array'"><legend>{{field.name}}</legend><table [formArrayName]=field.name"><tr><th *ngFor="let item of field.children">{{item.label}}</th><th><button class="btn btn-primary";(click)=getFormArray(field.name).push(this.addGroupArray(field.children))">添加</button></th></tr><tr *ngFor="let group of getFormArray(field.name).controls;let i=index";[formGroupName]=i"><td *ngFor="let item of field.children"><field-builder noLabel="true";[form]="getFormArray(field.name).at(i)";[字段]=项目"></field-builder></td><td><button class="btn btn-primary";(click)=getFormArray(field.name).removeAt(i)">删除</button></td></tr></fieldset>
我使用了两个仅返回casted"的辅助函数;formGroup 和 formArray
getFormGroup(field:string){返回 this.form.get(field) 作为 FormGroup}getFormArray(字段:字符串){返回 this.form.get(field) 作为 FormArray}
看看我们如何在内部调用自己的组件.我需要添加一个属性"noLabel 不显示标签,以防我们管理 FormArrayLabel
//在构造函数中构造函数(@Attribute('noLabel')noLabel){this.noLabel=noLabel ||错误的;}
并使用
我需要在这个组件中再次重复函数addGroupArray
(我想不出另一种方式)
好吧,唯一要考虑的是如何将值转换到formArray中,看到一个数组的字段是这样的:
<代码>{类型:数组",名称:数组名称",value:[{firstName:"array",lastName:"array lastName"}],孩子们: [{类型:文本",姓名:名字",标签:名字",要求:真实},{类型:文本",姓名:姓氏",标签:姓氏",要求:真实}]},
通常是 stackblitz 无保修
注意:选项的原子应该是
<div class="form-check";*ngFor="let opt of field.options"><label class="form-check-label"><input [formControlName]="field.name";类=表单检查输入"类型=收音机"[值]=opt.key";>{{opt.label}}标签>
更新 真的我从不喜欢拆分"具有真/假值的数组中的一系列复选框.该值更自然,例如c,f"并且选中复选框烹饪".和钓鱼".
好吧,首先是更改原子复选框";使用 [ngModel]
和 (ngModelChange)
.首先我们要创建两个辅助函数:
//一个简单的getter";获取价值获取值(){返回 this.form ?this.form.get(this.field.name).value : null;}//我们通过检查"和钥匙"选项的更改(检查:布尔值,键:任何){const oldvalue = this.form.get(this.field.name).value ||空值;//如果没有值如果(!旧值){//使用带有key"的setValue(如果选中)或 nullthis.form.get(this.field.name).setValue(checked ? "" + key : null);返回;} 别的 {//在值中存储满足条件的所有选项常量值 = 选中?this.field.options.filter(//在旧值中或者是keyx =>oldvalue.indexOf(x.key) >= 0 ||x.key == 键): this.field.options.filter(//在旧值中,不是键x =>oldvalue.indexOf(x.key) >= 0 &&x.key != 键);//如果没有满足的选项,我们给值 null//或键的连接这个.form.get(this.field.name).setValue(value.length > 0 ? value.map(x => x.key).join(",") : null);}}
好吧,现在我们可以使用我们的 [ngModel] 和 (ngModelChange).我们需要说 Angular 是一个独立的"
<div *ngFor="let opt of field.options";class="form-check form-check"><label class="form-check-label"><输入[ngModel]=值&&"value.indexOf(opt.key) >= 0"(ngModelChange)="change($event, opt.key)";[ngModelOptions]={独立:真实}";类=表单检查输入"类型=复选框"id="inlineCheckbox1";值=选项1"/>{{ opt.label }}</label>
因此,我们删除了复选框"的大小写;输入输出函数getForm
getForm(group: FormGroup, fields: any[]) {for(让 f 个字段){开关(f.type){案例组":...休息;案例数组":....休息;默认:...休息;}}}
I had implemented the dynamic form builder in my project and it is working fine. But, now I need to add a form inside the dynamic form builder. Which will be like form inside a form. So, I had created a formarray inside my ts and it is coming correctly.
I am getting an error like
ERROR Error: Cannot find control with name: '891713'
This is what I had implemented
https://angular.io/guide/dynamic-form
Code sample I used is https://stackblitz.com/edit/angular-dynamic-form-builder
So I had created a component named formControl inside the folder 'atoms' and if the control is fromControl, I am again calling field control.
FormControl component
<td *ngFor="let itm of items; let i = index" formArrayName="childs" >
<field-builder [field]="itm" [form]="form" ></field-builder>
</td>
FormControl component Ts
@Input() field: any = {};
@Input() form: FormGroup;
@Output() sendDataToParent = new EventEmitter<string>();
// form: FormGroup;
itemLength: any;
items: any;
elemData = {}
row = [];
isDisabled : boolean = false;
childs: FormArray;
formNewStatus : boolean = true;
// childs: any;
ngOnInit() {
this.items = this.field.items;
this.itemLength = this.items.length
this.items.forEach(element => {
const elemId =element.id;
this.elemData[elemId] = ''
});
console.log("from control form=====>",this.form)
this.row.push(this.elemData);
}
addTable() {
const frmArr = this.form.get('childs') as FormArray;
this.sendDataToParent.emit("add");
this.row.push(this.elemData)
console.log("this.row===>",this.row)
}
deleteRow(x){
this.row.splice(x, 1 );
}
deleteAllRow(){
console.log("came here")
this.row = [];
}
In the parent component, I had added the code for formarray to add more
onFormControlValueChange(parentField:any){
if(parentField === 'add'){
this.childs = this.form.get('childs') as FormArray;
this.childs.push(this.fb.group(this.createItem()));
}
console.log("this form====>", this.form)
}
Can anyone help me to solve this issue?
Thanks in advance! :)
解决方案 puff, it's a large and complex improve the code to allow fromGroups and formArray, but we are going to try.
First we are going to change a few the DynamicFormBuilderComponent
We are going to create a function getForm to can make recursive create formGroup
getForm(group: FormGroup, fields: any[]) {
for (let f of fields) {
switch (f.type) {
case "group":
group.addControl(f.name, new FormGroup({}));
this.getForm(group.get(f.name) as FormGroup, f.children);
break;
case "checkbox":
group.addControl(f.name, new FormGroup({}));
const groupOption = group.get(f.name) as FormGroup;
for (let opt of f.options) {
groupOption.addControl(opt.key, new FormControl(opt.value));
}
break;
case "array":
group.addControl(f.name, new FormArray([]));
const array = group.get(f.name) as FormArray;
if (f.value)
{
f.value.forEach(x=>array.push(this.addGroupArray(f.children)))
array.patchValue(f.value)
}
break;
default:
group.addControl(
f.name,
new FormControl(f.value || "", Validators.required)
);
break;
}
}
See that before we create an object fields
and finally add the object fields to the formGroup. Using this function we directly create an empty formGroup and we are adding FormControls or FormGroup or a FormArray using group.addControl
. This allow us call recursive the function if it's necesary.
So, in ngOnInit we make
ngOnInit() {
this.getForm(this.form, this.fields);
}
See that we can access to the form from parent using a template variable
<dynamic-form-builder #dynamic ..>
{{dynamic.form?.value|json}}
And how I decide that the form has the "exact" model -before we has an object with {"fields:"exactModel"}-
And we need an auxiliar function to create the formGroup of a FormArray
addGroupArray(fields:any[])
{
const group:FormGroup=new FormGroup({})
fields.forEach(x=>{
group.addControl(x.name,new FormControl(null,x.required?Validators.required:null))
})
return group
}
Our FieldBuilderComponent sould take account the new two types of fields: "array" and "group". I go to put it inside a fieldset
<!--in case group we repeat the field-builder using
as form the "getFormGroup" and as field "field.children"
-->
<fieldset *ngSwitchCase="'group'">
<legend>{{field.name}}</legend>
<field-builder *ngFor="let item of field.children"
[form]="getFormGroup(field.name)" [field]="item">
</field-builder>
</fieldset>
<!--in case array we create a table and use the function
"getFormArray"
-->
<fieldset *ngSwitchCase="'array'">
<legend>{{field.name}}</legend>
<table [formArrayName]="field.name">
<tr>
<th *ngFor="let item of field.children">{{item.label}}</th>
<th>
<button class="btn btn-primary" (click)="getFormArray(field.name).push(this.addGroupArray(field.children))">Add</button>
</th>
</tr>
<tr *ngFor="let group of getFormArray(field.name).controls;let i=index" [formGroupName]="i">
<td *ngFor="let item of field.children">
<field-builder noLabel="true" [form]="getFormArray(field.name).at(i)"
[field]="item">
</field-builder>
</td>
<td><button class="btn btn-primary" (click)="getFormArray(field.name).removeAt(i)">Delete</button></td>
</tr>
</table>
</fieldset>
I use two auxiliar function that only return "casted" the formGroup and the formArray
getFormGroup(field:string)
{
return this.form.get(field) as FormGroup
}
getFormArray(field:string)
{
return this.form.get(field) as FormArray
}
See how inside we call to the own component. I need add an "attribute" noLabel to not show the label in case we are mannage a FormArrayLabel
//in constructor
constructor(@Attribute('noLabel') noLabel) {
this.noLabel=noLabel || false;
}
And use
<label *ngIf="!noLabel" ....></label>
I need to repeat the function addGroupArray
again in this component (I can't imagine another way)
Well, the only to take account is how dive value to the formArray, see that the field of an array is like:
{
type: "array",
name: "arrayName",
value:[{firstName:"array",lastName:"array lastName"}],
children: [
{
type: "text",
name: "firstName",
label: "First Name",
required: true
},
{
type: "text",
name: "lastName",
label: "Last Name",
required: true
}
]
},
As usually this is the stackblitz without warranty
NOTE: the atom for options should be
<div [formGroup]="form">
<div class="form-check" *ngFor="let opt of field.options">
<label class="form-check-label">
<input [formControlName]="field.name" class="form-check-input" type="radio" [value]="opt.key" >
{{opt.label}}
</label>
</div>
</div>
Update Really I never like "split" a series of checkbox in an array with true/false values. It's more natural that the value was, e.g. "c,f" and is checked the check box "cooking" and "fishing".
Well, the first is change the "atom checkbox" using [ngModel]
and (ngModelChange)
. First we are going to create two auxiliar functions:
//a simple "getter" to get the value
get value() {
return this.form ? this.form.get(this.field.name).value : null;
}
//we pass "checked" and the "key" of the option
change(checked: boolean, key: any) {
const oldvalue = this.form.get(this.field.name).value || null;
//if has no value
if (!oldvalue) {
//use setValue with the "key" (if checked) or null
this.form.get(this.field.name).setValue(checked ? "" + key : null);
return;
} else {
//in value store all the options that fullfilled with the condition
const value = checked
? this.field.options.filter( //is in the old value or is the key
x => oldvalue.indexOf(x.key) >= 0 || x.key == key
)
: this.field.options.filter( //is in the old value and is not the key
x => oldvalue.indexOf(x.key) >= 0 && x.key != key
);
//we give the value null if there're no options that fullfilled
//or a join of the keys
this.form
.get(this.field.name)
.setValue(value.length > 0 ? value.map(x => x.key).join(",") : null);
}
}
Well, now we can use our [ngModel] and (ngModelChange). We need say to Angular that is a "standalone"
<div [formGroup]="form">
<div *ngFor="let opt of field.options" class="form-check form-check">
<label class="form-check-label">
<input
[ngModel]="value && value.indexOf(opt.key) >= 0"
(ngModelChange)="change($event, opt.key)"
[ngModelOptions]="{ standalone: true }"
class="form-check-input"
type="checkbox"
id="inlineCheckbox1"
value="option1"
/>
{{ opt.label }}</label>
</div>
</div>
So, we remove the case of "checkbox" in out function getForm
getForm(group: FormGroup, fields: any[]) {
for (let f of fields) {
switch (f.type) {
case "group":
...
break;
case "array":
....
break;
default:
...
break;
}
}
}
这篇关于带有formarray的角度动态表单生成器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文
登录
关闭
扫码关注1秒登录
发送“验证码”获取
|
15天全站免登陆