角形嵌套表格数组

时间:2018-11-29 12:15:10

标签: javascript angular typescript angular6 angular-reactive-forms

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

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

所以您正确理解了上面的工作示例概念?

我想使用相同的json,但不希望使用添加按钮和下拉

下拉列表

<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 = [
    {
      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" }
      ]
    }
  ]

还为上述https://stackblitz.com/edit/angular-1sg5cv

创建了 stackblitz 链接

如果我从下拉列表中选择选项template onetemplate two(因为选择框是多选),则我期望输出

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

如果单击模板1和2的两个选项,您将看到分别得到twothree输入框...

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

因此,只需将dropdown demo转换为static inputs with add button,就具有相同的嵌套json结构。

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

我在无法获得解决方案的过程中做到了最好,请帮助我根据下拉列表的选择来形成嵌套数组json。

2 个答案:

答案 0 :(得分:1)

@未定义,您需要两个不同的工作

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

第一部分是画架。逐步进行,如果您选择模板一,则需要类似

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

,但是您想做些灵巧的事,因此,使函数能够接收对象并返回FormGroup。由于您只需要模板和子项的“值”,因此您的功能可以像

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)
     })
}

因此我们仍然可以为差异模板创建一个formGroup并加入一个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);
    })
}

请注意,如果我们所有的模板子级都位于属性“子级”下(第一个不是templateOneChild,第二个不是templateOneChild ...),我们的函数将变为

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);
    })
}

好了,您已经创建了“表单”,现在该展示它了。形式就像

<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>

是的,我们有一个问题,我们不知道内部formArray的“ formControlName”。一种解决方案是使用变量“ controlsName”,该变量将是一个数组数组,因此,例如我们选择1和3模板,我们的controlsName就像

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

好吧,再次创建一个函数,该函数返回带有属性名称的字符串数组。这是我们createFormGroup的简单版本,可以接收“子项”并返回字符串数组。

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

好吧,在changeEvent中,我们在调用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);
    })

此后,替换<输入formControlName =“ ??????????” >通过

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

请注意我们使用[formControlName](而不是formControlName),因为它是一个计算表达式。

请参阅堆栈闪电here

答案 1 :(得分:0)

我不确定您的问题。您想使用json动态添加控件。

参考链接:https://angular.io/guide/dynamic-form

工作示例:https://stackblitz.com/edit/angular-srpk3w

用以下代码替换文件:

app.component.html

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

{{selectItems|json}}
<div *ngIf="form">
<form [formGroup]="form"> 
    <div *ngFor="let item of array">
            {{item.value}} is the parent
      <div *ngFor="let child of item.templateChild; index as i">
        {{child.property_name}}
        <input type="text" formControlName="{{child.property_name.split(' ').join('_')}}"  [value]="child.property_name" >
        </div> 
        <br><br><br>
      </div>
      </form>
      </div>
 <br><br><br>
{{form.value|json}}

app.component.ts

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

    array: any[] = [];
    selectItems: any;
    form: FormGroup;

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


  changeEvent(e) {
    this.array = [];
    for (let select of this.selectItems) {
     this.array.push(this.templates[select-1])
     this.form=this.getFormValue(this.array);
      }     
  }
getFormValue(array){
  let group: any = {};
  array.forEach(r=>{
    r.templateChild.forEach((t,index)=>{
    group[t.property_name.replace(/ /g, "_")]= new FormControl(t.property_name);
    })
  })
return new FormGroup(group);;
}
}