动态嵌套表单控件Angular 2

Posted

技术标签:

【中文标题】动态嵌套表单控件Angular 2【英文标题】:Dynamic nested form controls Angular 2 【发布时间】:2018-02-21 23:31:44 【问题描述】:

我的 API 响应是这样的

"groups": [
          "group_name": "GRP1"
          "attributes": [
                        "attribute_id": 1,
                        "attribute_name": "Frequency",
                        "value_reference": "tag"
                        ]
          ,
          
            "group_name": "GRP2"
           "attributes": [
                        "attribute_id": 2,
                       "attribute_name": "Date",
                        "value_reference": "static",
                        "value_static_type": "date"
                      ]
              ]  

-- 我如何在 Angular 4 中创建 formConrol 来显示像

这样的数据
GroupName  
   List of Group's Attribute  
GroupName
    List of Group's Attribute  

我最初的表单控件是这样的

this.editTemplateForm = this.fb.group(
            groups: this.fb.group(
                group_name: ['', Validators.required ],
                attributes : this.fb.group(
                    value_reference : []
                )
            ),
        );  

我不明白如何动态添加控件

【问题讨论】:

【参考方案1】:

如果您想完全匹配您的 API 响应,这里的结构最终会相当复杂。由于到目前为止,每个attributes 属性只有一个属性对象,因此您可以直接将attributes 设为对象,而不是对象数组,这将简化以下代码。下面的代码符合你当前的 API 结构,可以在this working plunker 玩。

注意事项:

FormGroup 需要是最外层的表单对象,也可以用来容纳不定数量的FormControl 项目 另一方面,FormArray 在控件的数量不确定时很有用,并且这些控件的名称并不重要。 这就是为什么,下面,allGroups 是一个包含最大组的数组,attributes 是一个包含您的属性对象的数组,但这些属性对象本身就是组 - 因为我们希望能够命名控件基于其 API 属性名称

这个结构也很有可能不是完全您正在寻找的(例如,也许您不希望所有这些值都是可编辑的<input> 字段),但它应该为您在 plunker 上使用打下坚实的基础,并了解如何根据不断变化的表单对象动态生成 html

import Component from '@angular/core';
import FormGroup, FormControl, FormArray, FormBuilder from '@angular/forms';

interface APIGroup 
    'group_name': string;
    'attributes': Array<GroupAttributes>

interface GroupAttributes 
    'attribute_id': number;
    'attribute_name': string;
    'value_reference': string;
    'value_static_type'?: string;


@Component(
  selector: 'app-child',
  template: `
    <div>
      <h3>I'm the Child component</h3>
    </div>
    <form [formGroup]="editTemplateForm">
        <div formArrayName="allGroups">
            <div *ngFor="let group of editTemplateForm.get('allGroups').controls; let i=index" [formGroupName]="i">
                <input formControlName="groupName" />
                <div formArrayName="attributes">
                    <div *ngFor="let attributeGroup of group.get('attributes').controls; let n=index" [formGroupName]="n">
                        <input *ngFor="let key of keysOfFormGroup(attributeGroup)" [formControlName]="key" />
                    </div>
                </div>
                <br/>
            </div>
        </div>
    </form>
    <pre style="background: #ddd">editTemplateForm.value | json</pre>
  `,
)
export class ChildComponent 
    constructor(
        private fb: FormBuilder
    )  

    sampleData: Array<APIGroup> = [
        
            "group_name": "GRP1",
            "attributes": [
                "attribute_id": 1,
                "attribute_name": "Frequency",
                "value_reference": "tag"
            ]
        ,
        
            "group_name": "GRP2",
            "attributes": [
                "attribute_id": 2,
                "attribute_name": "Date",
                "value_reference": "static",
                "value_static_type": "date"
            ]
        
    ]

    editTemplateForm: FormGroup;

    ngOnInit() 
        this.editTemplateForm = this.fb.group(
            allGroups: this.fb.array([])
        );

        // would call on a subscription to actual api data
        this.sampleData.forEach(group => 
            (<FormArray>this.editTemplateForm.get('allGroups'))
                .push(this.initGroup(group));            
        );
    

    initGroup(apiGroup: APIGroup): FormGroup 
        let formGroup = this.fb.group(
            groupName: [apiGroup.group_name],
            attributes: this.fb.array([])
        );
        apiGroup.attributes.forEach(attributeGroup => 
            (<FormArray>formGroup.get('attributes'))
                .push(this.initAttributeGroup(attributeGroup));
        );
        return formGroup;
    

    initAttributeGroup(attributes: GroupAttributes): FormGroup 
        let formGroup = this.fb.group();
        Object.keys(attributes).forEach(name => 
            formGroup.addControl(name, new FormControl(attributes[name]));
        );
        return formGroup;
    

    keysOfFormGroup(group: FormGroup): Array<string> 
        return Object.keys(group.controls);
    

【讨论】:

感谢@JackKoppa 的回复。你的回答肯定给了我一些想法,让我继续我的 API 响应。我还发现了另一个有用的 Plunker 链接 - plnkr.co/edit/hpuPdh?p=preview。再次感谢 对不起,上面的链接是你的。我发现的是 - plnkr.co/edit/3k23R3z2KHlAXW5OLKRA?p=preview 不错,那个笨拙的看起来也很适用。如果此答案足够,请随时将其标记为已接受,如果您有任何其他问题,请告诉我 嘿@JackKoppa 你知道我如何将新的表单组控件添加到“属性”吗?单击按钮时,我需要添加组和新的属性行。我设法添加新组,但不知道如何添加属性 应该像formArray.push()一样简单,传入新的FormGroup。让我尝试用那种按钮更新 plunk

以上是关于动态嵌套表单控件Angular 2的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 - 单元测试绑定到嵌套的自定义表单控件

在 Angular 中动态创建控件时不呈现 formControlName

Angular 2 - 自定义表单控件 - 禁用

动态禁用控件时显示警告消息 angular2

使用自定义验证和动态值的 Angular 表单

Angular 2 输入指令修改表单控件值