Angular学习笔记48:响应式表单-FormArray 和 FormGroup的多层嵌套

2023-11-16

Angular学习笔记46:响应式表单-使用FormBuild快速构建表单,可以使用FormBuilder快速便捷的构建出需要的表单。

有时候,在FormArray中,不仅仅是一个控件,有可能是多个,这个时候,这个FormArray中的元素就是一个FormGroup,并且这个FormGroup中的某一个实例又是一个FormArray,这样FormGroup和FormArray就会产生深层次的嵌套

FormGroup
FormControl
FormArray
FormControl
FormGroup
...
FormGroup
FormControl
FormArray
FormControl

有时会遇到这样的场景,模拟一个工作流。

FormGroup-工作流
FormControl-工作流名称
FormArray-工作流节点stages
FormControl-工作流类型
FormGroup-stage中的step
...
FormGroup-stage中的step
FormControl-step的名称
FormArray-step的内容
FormControl-step的类型

现在来实行它~

创建一个表单嵌套组件

wujiayudeMacBook-Pro:demo-test wjy$ ng g c form-nested
CREATE src/app/form-nested/form-nested.component.less (0 bytes)
CREATE src/app/form-nested/form-nested.component.html (30 bytes)
CREATE src/app/form-nested/form-nested.component.spec.ts (657 bytes)
CREATE src/app/form-nested/form-nested.component.ts (289 bytes)
UPDATE src/app/app.module.ts (1627 bytes)

修改模板文件:

<nz-divider [nzText]="'表单嵌套'"></nz-divider>

修改根组件,将这个组件显示出来

<!--<router-outlet></router-outlet>-->

<!--动画-->
<!--<app-animation-demo></app-animation-demo>-->
<!--<app-complex-animations></app-complex-animations>-->

<!--可编辑的table的Demo-->
<!--<app-edit-table></app-edit-table>-->

<!--响应式表单 FormGroup FormArray-->
<!--<app-user-info></app-user-info>-->

<!--多层级的表单嵌套-->
<app-form-nested></app-form-nested>

保存运行:

在这里插入图片描述

先构造出最上层(工作流)的实例

修改类文件:

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

@Component({
  selector: 'app-form-nested',
  templateUrl: './form-nested.component.html',
  styleUrls: ['./form-nested.component.less']
})
export class FormNestedComponent implements OnInit {
  public validateForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.validateForm = this.fb.group({
      workFlowName: [null, [Validators.required]],
      workFlowType: [null, [Validators.required]],
      workFlowContent: this.fb.array([
        this.fb.control(null)
      ])
    });
  }

  ngOnInit() {
  }

}

修改模板文件:

<nz-divider [nzText]="'表单嵌套'"></nz-divider>

<form [formGroup]="validateForm">

  <nz-form-item>
    <nz-form-label nzSpan="3" nz-col>
      工作流名称
    </nz-form-label>
    <nz-form-control nzSpan="7" nz-col>
      <input nz-input type="text" placeholder="请输入工作流名称" formControlName="workFlowName">
    </nz-form-control>

    <nz-form-label nzSpan="3" nzOffset="1" nz-col>
      工作流类型
    </nz-form-label>
    <nz-form-control nzSpan="7" nz-col>
      <input nz-input type="text" placeholder="请输入工作流类型" formControlName="workFlowType">
    </nz-form-control>
  </nz-form-item>

  <nz-form-item>
    <nz-form-label nzSpan="3" nz-col>
      工作流内容
    </nz-form-label>
    <nz-form-control nzSpan="14">
      <nz-row formArrayName="workFlowContent"
              *ngFor="let content of validateForm.controls['workFlowContent'].controls;
              let workFlowIndex = index">
        <nz-form-control nz-col nzSpan="12">
          <input nz-input [formControlName]="workFlowIndex" type="text">
        </nz-form-control>

      </nz-row>
    </nz-form-control>
  </nz-form-item>

</form>

<nz-form-item>
  <nz-form-label nzSpan="3" nz-col>
    表单的值
  </nz-form-label>
  <nz-form-control nzSpan="21" nz-col>
    {{validateForm.value | json}}
  </nz-form-control>
</nz-form-item>

保存运行以后,发现最上层(工作流)的实例已经实现:

在这里插入图片描述

构造出工作流内容(workFlowContent)中的的实例

将validateForm中的workFlowContent实例中的this.fb.control(null),转换成this.fb.group({})。
在这个this.fb.group({})中添加workFlowContent(FormGroup的实例).

  1. 修改workFlowContent(FormGroup的实例):
workFlowContent: this.fb.array([
        this.fb.group({
          stageName: [null, [Validators.required]],
          stageType: [null, [Validators.required]],
          stageContent: this.fb.array([
            this.fb.control(null)
          ]),
        })
      ])

现在以:stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这三个为一个FormGroup作为workFlowContent的其中一个实例。

  1. 修改模板文件
    注意,在这里有两种写法
    第一种:将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这些组成的 FormGroup 的 FormGroupName用一个div表示,将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)包在这个div中。
<nz-form-control nzSpan="18">
      <div class="workFlowContent" formArrayName="workFlowContent"
           *ngFor="let content of validateForm.controls['workFlowContent'].controls;
              let workflowIndex = index">
        <div [formGroupName]="workflowIndex.toString()">
          <nz-row>
            <nz-form-label nzSpan="3" nz-col>
              stage{{workflowIndex + 1}}
            </nz-form-label>
          </nz-row>
          <nz-row>
            <nz-form-label nzSpan="3" nz-col>stageName</nz-form-label>
            <nz-form-control nz-col nzSpan="7">
              <input nz-input type="text" formControlName="stageName">
            </nz-form-control>
            <nz-form-label nzOffset="1" nzSpan="3" nz-col>stageType</nz-form-label>
            <nz-form-control nz-col nzSpan="7">
              <input nz-input type="text" formControlName="stageType">
            </nz-form-control>
          </nz-row>

          <nz-row>
            <nz-form-label nzSpan="3" nz-col>stageContent</nz-form-label>
            <nz-form-control nz-col nzSpan="18">
              <nz-row formArrayName="stageContent"
                      *ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
                <input nz-input [formControlName]="stageIndex.toString()">
              </nz-row>
            </nz-form-control>
          </nz-row>
        </div>
      </div>
    </nz-form-control>

第二种:将stageName(FormControl)、stageType(FormControl)、stageContent(FormArray)这些组成的 FormGroup 的信息分别写在对应的FormControl的外面。

<nz-form-control nzSpan="18">
      <div class="workFlowContent" formArrayName="workFlowContent"
           *ngFor="let content of validateForm.controls['workFlowContent'].controls;
              let workflowIndex = index">
        <nz-row>
          <nz-form-label nzSpan="3" nz-col>
            stage{{workflowIndex + 1}}
          </nz-form-label>
        </nz-row>
        <nz-row>
          <nz-form-label nzSpan="3" nz-col>stageName</nz-form-label>
          <nz-form-control nz-col nzSpan="7" [formGroupName]="workflowIndex.toString()">
            <input nz-input type="text" formControlName="stageName">
          </nz-form-control>
          <nz-form-label nzOffset="1" nzSpan="3" nz-col>stageType
          </nz-form-label>
          <nz-form-control nz-col nzSpan="7" [formGroupName]="workflowIndex.toString()">
            <input nz-input type="text" formControlName="stageType">
          </nz-form-control>
        </nz-row>

        <nz-row>
          <nz-form-label nzSpan="3" nz-col>stageContent</nz-form-label>
          <nz-form-control nz-col nzSpan="18" [formGroupName]="workflowIndex.toString()">
            <nz-row formArrayName="stageContent"
                    *ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
              <input nz-input [formControlName]="stageIndex.toString()">
            </nz-row>
          </nz-form-control>
        </nz-row>
      </div>
    </nz-form-control>

增加一点点样式,以突出显示stage信息

.workFlowContent {
  background: #e6f7ff;
  border: 1px solid #91d5ff;
  padding: 10px;
  border-radius: 4px;
}

保存运行以后,就会发现,原来的workFlowContent数组中的元素由一个单纯的’string’ 类型办成了 ‘object’ 类型,并且这个object中的key都是FormGroup中的每一个实例。

在这里插入图片描述

  1. 添加两个按钮,使FormArray:workFlowContent 可以动态的增减。

为了方便,将 workFlowContent 使用get 将其装换成FormArray;

 get workFlowContent() {
    return this.validateForm.get('workFlowContent') as FormArray;
  }

然后修改模板文件,使用属性workFlowContent;
将原来的

 <div class="workFlowContent" formArrayName="workFlowContent"
           *ngFor="let content of validateForm.controls['workFlowContent'].controls;
              let workflowIndex = index">
              ...
              ...
</div>

修改为:

<div class="workFlowContent" formArrayName="workFlowContent"
           *ngFor="let content of workFlowContent.controls;
              let workflowIndex = index">
              ...
              ...
</div>

a. 添加 ‘增加stage’ 的按钮,并绑定一个点击事件

模板文件:

<button nz-button (click)="addStage()">增加stage</button>

类文件:

public addStage(): void {
    this.workFlowContent.push(
      this.fb.group({
        stageName: [null, [Validators.required]],
        stageType: [null, [Validators.required]],
        stageContent: this.fb.array([
          this.fb.control(null)
        ]),
      })
    );
  }

保存运行:

在这里插入图片描述

b. 添加 ‘删除’ 的按钮,并绑定一个点击事件

模板文件:

<nz-row>
    <nz-form-label nzSpan="3" nz-col>
            stage{{workflowIndex + 1}}
    </nz-form-label>
    <nz-form-control nzOffset="15" nzSpan="3" nz-col>
        <button nz-button (click)="removeStage(workflowIndex)">
              删除
        </button>
    </nz-form-control>
</nz-row>

类文件:

public removeStage(workflowIndex: number): void {
    this.workFlowContent.removeAt(workflowIndex);
  }

保存运行:

在这里插入图片描述

构造出stage内容(stageContent)中的的实例

将 workFlowContent 中的每一个 stageContent 实例中的this.fb.control(null),转换成this.fb.group({})。
在这个this.fb.group({})中添加workFlowContent(FormGroup的实例).

  1. 修改workFlowContent(FormGroup的实例):
stageContent: this.fb.array([
            this.fb.group({
              stepName: [null, [Validators.required]],
              stepType: [null, [Validators.required]],
              stepContent: this.fb.array([
                this.fb.control(null)
              ])
            })
          ]),

现在以:stepName(FormControl)、stepType(FormControl)、stepContent(FormArray)这三个为一个FormGroup作为 stageContent 的其中一个实例。

由于stageContent是workFlowContent中的实例,所以在修了初始化的部分,还需要修改增加按钮的点击事件

public addWorkflow(): void {
    this.workFlowContent.push(
      this.fb.group({
        stageName: [null, [Validators.required]],
        stageType: [null, [Validators.required]],
        stageContent: this.fb.array([
          this.fb.group({
            stepName: [null, [Validators.required]],
            stepType: [null, [Validators.required]],
            stepContent: this.fb.array([
              this.fb.control(null)
            ])
          })
        ]),
      })
    );
  }
  1. 修改模板文件
 <nz-row>
          <nz-row>
            <nz-form-label nzSpan="3" nz-col>
              stageContent
            </nz-form-label>
          </nz-row>
          <nz-row>
            <nz-form-control nzOffset="3" nzSpan="18" [formGroupName]="workflowIndex.toString()">
              <div formArrayName="stageContent"
                   class="stageContent"
                   *ngFor="let stage of content.get('stageContent').controls;let stageIndex = index">
                <div [formGroupName]="stageIndex.toString()">
                  <nz-row>
                    <nz-form-label nzSpan="3" nz-col>step{{stageIndex + 1}}</nz-form-label>
                  </nz-row>
                  <nz-row>
                    <nz-form-label nzSpan="3" nz-col>stepName</nz-form-label>
                    <nz-form-control nzSpan="7" nz-col>
                      <input nz-input type="text" formControlName="stepName" placeholder="请输入stepName">
                    </nz-form-control>

                    <nz-form-label nzOffset="1" nzSpan="3" nz-col>stepType</nz-form-label>
                    <nz-form-control nzSpan="7" nz-col>
                      <input nz-input type="text" formControlName="stepType" placeholder="请输入stepType">
                    </nz-form-control>
                  </nz-row>
                  <nz-row>
                    <nz-form-label nzSpan="3" nz-col>stepContent</nz-form-label>
                    <nz-form-control nzSpan="7" nz-col formArrayName="stepContent">
                      <nz-row *ngFor="let step of stage.get('stepContent').controls;let stepIndex = index">
                        <input nz-input type="text"
                               [formControlName]="stepIndex.toString()"
                               placeholder="请输入stepContent">
                      </nz-row>
                    </nz-form-control>
                  </nz-row>
                </div>
              </div>
            </nz-form-control>
          </nz-row>

        </nz-row>

为了方便区别,在step增加一些样式:

.stageContent {
  background: #fffbe6;
  border: 1px solid #ffe58f;
  padding: 10px;
  border-radius: 4px;
  margin-bottom: 10px;
}

运行保存

在这里插入图片描述

  1. 添加两个按钮,使FormArray:stageContent 可以动态的增减。

a. 添加 ‘增加step’ 按钮

修改模板文件

 <button nz-button (click)="addStep(workflowIndex)">增加step</button>

在类文件中增加 addStep() 方法

// add stage
  public addStep(workflowIndex: number): void {
    (this.workFlowContent.at(workflowIndex).get('stageContent') as FormArray).push(
      this.fb.group({
        stepName: [null, [Validators.required]],
        stepType: [null, [Validators.required]],
        stepContent: this.fb.array([
          this.fb.control(null)
        ])
      }));
  }

b. 添加 ‘删除step’ 按钮
修改模板文件

<nz-row>
  <nz-form-label nzSpan="3" nz-col>step{{stageIndex + 1}}</nz-form-label>
  <nz-form-control nzOffset="15" nzSpan="3">
    <button nz-button (click)="removeStep(workflowIndex,stageIndex)">删除step</button>
  </nz-form-control>
</nz-row>

在类文件中增加 removeStep() 方法

  removeStep(workflowIndex: number, stageIndex: number) {
    (this.workFlowContent.at(workflowIndex).get('stageContent') as FormArray).removeAt(stageIndex);
  }

在这种情况下,是将取到workFlowContent 中第 workflowIndex 个 FormGroup,在这个FormGroup的stageContent实例中中找到第stageIndex个stageContent中的FormGroup,然后将其删除。

为了区别stage和step的删除,将原来删除stage 的按钮修改为’删除stage’,
保存运行以后

在这里插入图片描述

  1. 添加两个按钮,使FormArray:stepeContent 可以动态的增减。

在这里使用一种简单的方式,不在关注index,而是直接使用遍历出来的元素

a. 添加 ‘增加stepContent’ 按钮

修改模板文件

<nz-row>
    <button nz-button (click)="addStepContent(stage)">
        添加StepContent
    </button>
</nz-row>

由于StepContent 是 stage 实例中的,所以只要将 stage的 stepContent as FormArray,然后在其中增加

在类文件中增加 addStepContent() 方法

// add StepContent
 public addStepContent(stage: FormGroup) {
    (stage.get('stepContent') as FormArray).push(this.fb.control(null));
  }

b. 添加 ‘删除step’ 按钮(采用和增加StepContent一样的思路进行删除操作)
修改模板文件

<nz-form-control nzSpan="4">
    <button nz-button (click)="removeStepContent(stage,stepIndex)">
        删除StepContent
    </button>
</nz-form-control>

在类文件中增加 removeStep() 方法

 // remove StepContent
  public removeStepContent(stage: FormGroup, stepIndex: number): void {
    (stage.get('stepContent') as FormArray).removeAt(stepIndex);
  }

保存运行:

在这里插入图片描述
这样深层嵌套的表单就完成了。

代码已上传GitHub中的form-nested组件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular学习笔记48:响应式表单-FormArray 和 FormGroup的多层嵌套 的相关文章

随机推荐

  • STM32 ADC采样不准怎么办

    最近在使用STM32L011的ADC时 发现ADC采样值极其不准确 经过查找资料 和反复测试 总算摸出点门道 如将VDDA接到VDD 那么电源的波动会极大的影响ADC精度 不过使用内部参考电压可以计算实际的VDDA电压 说明 VREFINT
  • Ubuntu 16.04下基于Anaconda安装cuda、cudnn及Pytorch

    Anaconda的基本使用见之前的博客 Ubuntu 16 04下Anaconda的安装及使用 创建虚拟环境pytorch gpu 并激活该环境 conda create name pytorch gpu python 3 6 source
  • CSS浮动(1)

    一 结构伪类选择器 1 作用与优势 作用 根据元素在HTML中的结构关系查找元素 优势 减少对于HTML中类的依赖 有利于保持代码整洁 场景 常用于查找某父级选择器中的子元素 2 选择器 选择器 说明 E first child 匹配父元素
  • python 数据库的中文乱码问题

    在使用pymssql操作sqlserver数据库时 碰到了中文乱码的问题 之前从网上查了很多 又是python编码 又是数据库编码什么的 非常乱 试了很多 好多都解决不了问题 数据库是sqlserver 先说我碰到的问题 数据库的配置cha
  • 【安卓学习之常见问题】 AAPT: error: resource drawable/ (aka xxx) not found(含as快捷键说明)

    安卓学习之常见问题 AAPT error resource drawable aka xxx not found 含as快捷键说明 相关文章 android学习开源项目之BasePopup BasePopup PopupWindow 进行二
  • c语言编程题

    include stdafx h include
  • 概念解析

    注1 本文系 概念解析 系列之一 致力于简洁清晰地解释 辨析复杂而专业的概念 本次辨析的概念是 非极大值抑制 NMS 及其改进工作 非极大值抑制 NMS 原理 缺点和改进 1 背景介绍 在计算机视觉中 物体检测是一个核心且充满挑战的问题 众
  • MQTT 协议基本介绍

    目录 一 简介 二 基本特点 三 基本概念 四 简单示例 五 进一步了解MQTT 3 六 MQTT协议的工作方式 七 MQTT控制报文 CONNECT报文 CONNACK报文 八 清除会话 保留消息和QoS的组合 九 MQTT 5 0 协议
  • Hyperledger Fabric环境搭建流程记录详解

    Fabric环境搭建记录 什么是超级账本Fabric 1 Fabric的本质 与一般区块链技术的相同点 Fabric与其他区块链技术一样 都有一个账本 和以太坊相像 也允许使用智能合约 从本质上看 它是参与者共同管理交易的系统 是联盟链的典
  • python分析excel数据-对照Excel使用Python进行数据分析,更快掌握

    Excel和Python 作为数据分析的主流工具 在从效率提升到数据商业化的整个过程中 都起到了重要作用 不管是在Excel中通过鼠标点选实现 亦或是利用Python通过代码实现 数据分析中的很多基础功能都是相通的 在数据量级大跃进的今天
  • HTTP协议原理

    一 http协议是什么 HTTP协议 全称HyperText Transfer Protocol 中文名为超文本传输协议 是互联网中最常用的一种网络协议 广泛应用于Web浏览器和Web服务器之间的应用层通信协议 在Internet上的Web
  • 基于flask与tk的网络粘贴板

    基于flask与tk的网络粘贴板 由于本人一直用 linux 所以平时和我的队友们交流代码 十分的不方便 没有办法 我就自己写一个功能简单但是特别适合我自己用的一个网络粘贴板 只有两个按钮 下载 和 上传 我的这个应用分成两个部分 一个是服
  • MFC之树形控件25

    1 树形控件 先了解相关知识 1 创建基于对话框的项目 2 删除对话框原有的内容 添加树形控件TreeControl 3 右击树形控件属性 然后添加相应属性 4 在资源视图里面右击icon文件夹 添加资源 选择icon导入预先准备好的图片
  • idea plugins一直在转圈解决方法

    方案一 配置代理 https plugins jetbrains com 然后重启idea 方案二 打开ip查看网站查看plugins jetbrains com的ip ip查看 然后将ip配置到host文件中 打开host文件 C Win
  • 胖AP与瘦AP的区别以及胖瘦AP组网的优劣对比

    一 胖瘦AP如何区分 无线AP通常可以分为胖AP Fat AP 和瘦AP Fit AP 两类 不是以外观来分辨的 而是从其工作原理和功能上来区分 当然 部分胖 瘦AP在外观上确实能分辨 比如有WAN口的一定是胖AP 胖AP除了前面提到的无线
  • 算法编程题-字符串类型题目

    1 介绍 在笔试面试中 字符串类型题目相当广泛 原因有一下几点 1 字符串可以看做是字符类型的数组 与数组的排序 查找 调整有关 2 很多其他类型的题目最终可能会转化成字符串类型的题目 2 需要掌握的几个概念 1 回文 2 子串 连续 3
  • NLP实践——VQA/Caption生成模型BLIP-2的应用介绍

    NLP实践 VQA Caption生成模型BLIP 2的应用介绍 1 简介 2 模型下载 3 运行环境 4 模型应用 1 简介 今天介绍一个跨模态模型 也是最近比较火的一个工作 叫做BLIP 2 很久很久之前我写过一个简单的image ca
  • ubuntu14.04安装CUDA7.0、CUDNN7.0详细步骤

    一 安装CUDA7 0 CUDA 7 0在Linux下的安装步骤参见官网手册 CUDA Getting Started Linux 其中提及了 run deb 等安装 1 检查你的电脑是否支持CUDA 检查GPU是否支持 输入如下命令 如果
  • uniapp运行到小程序报错之[ app.json 文件内容错误] app.json: app.json 未找到

    解决方法 在project config json文件下面新增如下代码 unpackage dist dev mp weixin是你自己的代码 打包下dist文件夹下的项目名称 和pages同级的文件夹 miniprogramRoot un
  • Angular学习笔记48:响应式表单-FormArray 和 FormGroup的多层嵌套

    继Angular学习笔记46 响应式表单 使用FormBuild快速构建表单 可以使用FormBuilder快速便捷的构建出需要的表单 有时候 在FormArray中 不仅仅是一个控件 有可能是多个 这个时候 这个FormArray中的元素