角度嵌套的表单数组 [英] Nested form array in angular

查看:32
本文介绍了角度嵌套的表单数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作带有反应形式的角度应用程序,其中我制作了一个嵌套的表单数组,该数组将在按钮单击时嵌套.

一个干净的工作示例https://stackblitz.com/edit/angular-thhczx ,它有静态输入,因此点击Add new template,它会添加另一个嵌套部分,对于Add new property,它会生成另一个属性数组..

所以你已经掌握了上面的工作示例概念对​​吗??

我想要相同的 json,但不想要添加按钮和 dropdown.

下拉菜单的 Html:

<选择多个 [(ngModel)]="selectItems" (change)="changeEvent($event)"><option *ngFor="let template of templates" [value]="template.key">{{template.value}}</option></选择>{{selectItems|json}}<form [formGroup]="form"><div *ngFor="让数组项">{{item.value}} 是父项<div *ngFor="let child of item.templateChild">{{child.property_name}}<input type="text" [value]="child.property_name">

<br><br><br>

</表单><br><br><br>{{form.value|json}}

模板数组:为下拉菜单提供值

 模板 = [{键:1,值:模板一",模板一个孩子:[{ property_name: "属性一" },{ property_name: "属性二" }]},{键:2,值:模板二",templateTwoChild: [{ property_name: "属性三" },{ property_name: "属性四" },{ property_name: "属性五" }]},{键:3,值:模板三",模板三孩子:[{ property_name: "属性六" },{ property_name: "属性七" }]}]

还为上述 https://stackblitz 制作了一个 stackblitz 链接.com/edit/angular-1sg5cv

在这里,如果我从下拉列表中选择选项 template onetemplate two(因为选择框是多选),那么我期待输出如,

"template_details" : [{ "template_name": "模板一",模板数据":[{property_one":",property_two":"}]},{ "template_name": "模板二",模板数据":[{property_three":",property_four":","property_five":""}]}]

点击模板一、二这两个选项可以看到分别得到twothree输入框...

我希望在选择下拉值时在每个模板下自动生成带有属性名称的输入框..

所以简单地需要转换下拉演示就像带有添加按钮的静态输入,具有相同的嵌套 json 结构.

我恳请角度专家帮助我根据所选模板的属性名称生成输入框..

我尽力了,但无法获得解决方案,请帮助我根据下拉列表的选择形成嵌套数组 json..

解决方案

@Undefined,你需要两个不同的工作

  1. 创建一个表单组
  2. 显示管理 formGroup 的输入

第一部分是更简单的部分.一步一步来,如果你选择模板之一,你需要一些像

this.fb.group({template_name:"模板一",模板数据:this.fb.array([这个.fb.group({property_one:'',property_two:''})])})

但是你想动态地做这些事情,所以,创建一个接收一个对象并返回一个 FormGroup 的函数.由于您只需要模板和孩子的价值",您的功能可以像

createFormGroup(value:string,children:any[]):FormGroup{/*例如.对于模板一,您发送值:模板一",孩子们: [{ property_name: "属性一" },{ property_name: "属性二" }]*/让控件:FormGroup[]=children.map((x:any)=>this.fb.group({[x.property_name]:''}))返回 this.fb.group({模板名称:值,模板数据:this.fb.array(控件)})}

所以我们可以为不同的模板创建一个 formGroup 并加入一个 FormArray

changeEvent(e) {让 arrayControls:FormGroup[] = [];//在 this.selectItems 我们有,例如[1,3]for(让选择 this.selectItems){//搜索模板,选择将是例如1,3让模板:any=this.templates.find(x=>x.key==select);开关(+选择){情况1:arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));休息;案例2:arrayControls.push(this.createFormGroup(template.value,template.templateTwoChild));休息;案例3:arrayControls.push(this.createFormGroup(template.value,template.templateThreeChild));休息;}}this.form=this.fb.group({template_details:this.fb.array(arrayControls);})}

看到如果我们所有的模板子代都在属性children"下(第一个不是 templateOneChild ,第二个是 templateTwoChild ......)我们的函数就变成了

changeEvent(e) {让 arrayControls:FormGroup[] = [];//在 this.selectItems 我们有,例如[1,3]for(让选择 this.selectItems){//搜索模板,选择将是例如1,3让模板:any=this.templates.find(x=>x.key==select);arrayControls.push(this.createFormGroup(template.value,template.children));}this.form=this.fb.group({template_details:this.array(arrayControls);})}

您已经创建了表单",现在是展示它的时候了.形式就像

<form [formGroup]="form"><div formArrayName="template_details"><div *ngFor="let item of details.controls;let i=index" [formGroupName]="i"><input formControlName="template_name"><div formArrayName="template_data"><div *ngFor="let child of item.get('template_data').controls;let j=index" [formGroupName]="j"><input formControlName="??????????">

</表单>

是的,我们有一个问题,我们不知道内部formArray的formControlName".一种解决方案是有一个变量controlsName",它将是一个数组数组,因此,如果例如我们选择 1 和 3 模板我们的控件名称就像

controlsName=[["property_one","property_two"],["property_six",property_seven"]]

好吧,再次创建一个函数,该函数返回带有属性名称的字符串数组.它是我们 createFormGroup 的一个简单版本,接收children"并返回一个字符串数组.

getControlNames(children:any[]):string[]{让 controlNames:string[]=children.map(x=>x.property_name);返回控件名称;}

好吧,在changeEvent中,我们在调用createFormGroup之后调用这个函数

changeEvent(e) {让 arrayControls:FormGroup[] = [];让控件名称:字符串[] = [];//<--添加这一行for(让选择 this.selectItems){让模板:any=this.templates.find(x=>x.key==select);开关(+选择){情况1:arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));controlName.push(this.getControlNames(template.templateOneChild));//<--还有这个休息;...同上案例 2 和案例 3 ...}}this.controlsName=控件名称;//<--先给名称赋值//然后创建表单this.form=this.fb.group({template_details:this.fb.array(arrayControls);})

在此之后,替换 <输入 formControlName="??????????"> 由

看到我们使用 [formControlName](不是 formControlName),因为它是一个求值的表达式.

查看堆栈闪电战此处

I am making angular application with reactive form, where i have made a nested form array which will get nested on button click.

A clean working example https://stackblitz.com/edit/angular-thhczx , it has static inputs and hence on click over Add new template, it will add a another nested part and for Add new property, it will generate another property array..

So you had got the above working example concept right??

I would like to have the same json but not with add button and with dropdown.

Html of dropdown:

<select multiple [(ngModel)]="selectItems" (change)="changeEvent($event)">
      <option *ngFor="let template of templates" [value]="template.key">{{template.value}}</option>
</select>

{{selectItems|json}}

<form [formGroup]="form">
    <div *ngFor="let item of array">
            {{item.value}} is the parent
      <div *ngFor="let child of item.templateChild">
        {{child.property_name}}
        <input type="text" [value]="child.property_name">
        </div>
        <br><br><br>
      </div>
      </form>
 <br><br><br>
{{form.value|json}}

Templates array: which gives value for dropdown

    templates = [
    {
      key: 1, value: "Template one",
      templateOneChild: [
        { property_name: "Property one" },
        { property_name: "Property two" }
      ]
    },
    {
      key: 2, value: "Template two",
      templateTwoChild: [
        { property_name: "Property three" },
        { property_name: "Property four" },
        { property_name: "Property five" }
      ]
    },
    {
      key: 3, value: "Template three",
      templateThreeChild: [
        { property_name: "Property six" },
        { property_name: "Property seven" }
      ]
    }
  ]

Also made a stackblitz link for the above https://stackblitz.com/edit/angular-1sg5cv

Here if i select the option template one and template two (as the selectbox is multi select) from the dropdown then i am expecting the output as,

"template_details" : [
    { "template_name": "Template One",
      "template_data" : [{"property_one": "", "property_two":""}]
    },
    { "template_name": "Template Two",
      "template_data" : [{"property_three": "", "property_four":"", 
                            "property_five":""}]
    }
    ]

If you click over the two options of template one and two you can see that you will get two and three input boxes respectively...

I wish to generate the input boxes with property names automatically under each template on selection of dropdown values..

So in simple need to convert dropdown demo as like the static inputs with add button with the same nested json structure.

I kindly request angular experts to help me in generation of input boxes based on property names for the selected template's..

I did my level best in it unable to get the solution please help me to form nested array json on based on selection of dropdown..

解决方案

@Undefined, you need two different jobs

  1. Create a formGroup
  2. Display inputs that manage the formGroup

the first part is the easer. Go step by step, if you select template one, you need some like

this.fb.group({
    template_name:"template one",
    template_data:this.fb.array([
          this.fb.group({
             property_one:'',
             property_two:''
          })
    ])
})

but you want to do the things dinamically, so, make a function that receive an object and return a FormGroup. As you only need the "value" of the template and the childs, your function can be like

createFormGroup(value:string,children:any[]):FormGroup
{
/*e.g. for template one, you send 
      value: "Template one",
      children: [
        { property_name: "Property one" },
        { property_name: "Property two" }
      ]
*/

     let controls:FormGroup[]=children.map(
          (x:any)=>this.fb.group({
              [x.property_name]:''
            })
     )
     return this.fb.group({
         template_name:value,
         template_data:this.fb.array(controls)
     })
}

So yet we can create a formGroup for the differents templates and join in a FormArray

changeEvent(e) {
    let arrayControls:FormGroup[] = [];
    //in this.selectItems we have, e.g. [1,3]
    for (let select of this.selectItems) {
      //search the template, select will be e.g. 1,3
      let template:any=this.templates.find(x=>x.key==select);
      switch (+select) {
        case 1:
          arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));
          break;
        case 2:
          arrayControls.push(this.createFormGroup(template.value,template.templateTwoChild));
          break;
        case 3:
          arrayControls.push(this.createFormGroup(template.value,template.templateThreeChild));
          break;
       }
    }
    this.form=this.fb.group({
       template_details:this.fb.array(arrayControls);
    })
}

See that if all ours children of templates was under a property "children" (not templateOneChild for the first, templateTwoChild for the seconds...) our function becomes in

changeEvent(e) {
    let arrayControls:FormGroup[] = [];
    //in this.selectItems we have, e.g. [1,3]
    for (let select of this.selectItems) {
      //search the template, select will be e.g. 1,3
      let template:any=this.templates.find(x=>x.key==select);
      arrayControls.push(this.createFormGroup(template.value,template.children));
    }
    this.form=this.fb.group({
       template_details:this.array(arrayControls);
    })
}

Well you have the "form" created, now is time to show it. The form is like

<div *ngIf="form">
  <form [formGroup]="form">
    <div formArrayName="template_details">
      <div *ngFor="let item of details.controls;let i=index" [formGroupName]="i">
        <input formControlName="template_name">

        <div formArrayName="template_data">
          <div *ngFor="let child of item.get('template_data').controls;let j=index" [formGroupName]="j">
         <input formControlName="??????????">
          </div>
        </div>
      </div>
    </div>
  </form>
</div>

Yes, we have a problem, we don't know the "formControlName" of the inner formArray. One solution is have a variable "controlsName" that will be an array of array, so, if e.g. we choose 1 and 3 template our controlsName was like

controlsName=[
   ["property_one","property_two"],
   ["property_six",property_seven"]
]

Well, again make a function that return an array of strings with the names of the properties. it's a simply version of our createFormGroup, receive "children" and return an array of strings.

getControlNames(children:any[]):string[]
{
     let controlNames:string[]=children.map(x=>x.property_name);
     return controlNames;
}

Well, in changeEvent we call to this function after call to createFormGroup

changeEvent(e) {
    let arrayControls:FormGroup[] = [];
    let controlsName:string[] = []; //<--add this line
    for (let select of this.selectItems) {
      let template:any=this.templates.find(x=>x.key==select);
      switch (+select) {
        case 1:
          arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));
           controlsName.push(this.getControlNames(template.templateOneChild)); //<--and this
          break;
        ... idem with case 2 and case 3...
      }
    }
    this.controlsName=controlsName; //<--give value to name first
    //then create the form
    this.form=this.fb.group({
       template_details:this.fb.array(arrayControls);
    })

After this, replace the < input formControlName="??????????" > by

<input [formControlName]="controlsName[i][j]"> 

See that we use [formControlName] (not formControlName) because is an evaluated expression.

See the stackblitz here

这篇关于角度嵌套的表单数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆