Angular 响应式表单-FormArray 和 FormGroup的多层嵌套

2023-05-16

由于在工作中需要做多层的表单提交校验功能,但一直没有好的方法 ,查找了一下网上资料刚好有解决的办法 , 所以借鉴了一下并收藏下来 , 做以后再次使用

有时候,在FormArray中,不仅仅是一个控件,有可能是多个,这个时候,这个FormArray中的元素就是一个FormGroup,并且这个FormGroup中的某一个实例又是一个FormArray,这样FormGroup和FormArray就会产生深层次的嵌套
在这里插入图片描述
有时会遇到这样的场景,模拟一个工作流。
在这里插入图片描述
现在我们来开始学习它

创建一个表单嵌套组件

 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的其中一个实例。

2.修改模板文件
注意,在这里有两种写法
第一种:将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中的每一个实例。
在这里插入图片描述
3.添加两个按钮,使FormArray:workFlowContent 可以动态的增减。
为了方便,将 workFlowContent 使用get 将其装换成FormArray;

import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';

  get workFlowContent(): FormArray {
    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)
            ])
          })
        ]),
      })
    );
  }

2.修改模板文件

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

在这里插入图片描述
3.添加两个按钮,使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’,
保存运行以后
在这里插入图片描述
4.添加两个按钮,使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);
  }

保存运行:

在这里插入图片描述
这样深层嵌套的表单就完成了。在此感谢博主…

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

Angular 响应式表单-FormArray 和 FormGroup的多层嵌套 的相关文章

  • python自然语言处理之spacy详解

    spaCy简介 spaCy号称工业级Python自然语言处理 xff08 NLP xff09 软件包 xff0c 可以对自然语言文本做词性分析 命名实体识别 依赖关系刻画 xff0c 以及词嵌入向量的计算和可视化等 spaCy模块有4个非常
  • anaconda创建env报错 ResolvePackageNotFound

    具体错误 如图 xff1a 按照其他博主 xff08 方法详情 xff09 提供的方法操作了还是有部分报错 xff1a 解决策略 继续上面解决剩下的部分报错 xff0c 打开 yaml文件 xff0c 记事本打开就行 将报错列出的几个包移到
  • LDA主题建模过程及参数详解

    平台及工具 语言 xff1a python 平台 xff1a anaconda 43 jupyter notebook 语料库 xff1a 近三百篇英文文献的摘要 主要代码 首先 xff0c pandas处理csv数据 span class
  • 已经成功安装了但是jupyter notebook仍然找不到模块

    问题描述 工具 语言 jupyter notebook 43 anaconda python 有时会遇到这样的情况 xff0c 命名已经install了模块 xff0c notebook还是报找不到模块错误 再装已经提示satisfied
  • pyecharts 地图绘制

    环境描述 win11 jupyter notebook 目标效果 世界地图 43 按数据进行分级着色 xff1b 最终效果图如下 xff1a pyecharts 绘制地图时注意点 可以实现目标地图绘制效果的python库很多 xff0c 这
  • 指定wb用户在指定日期范围内的wb内容抓取

    一 操作步骤 只记录过程 xff0c 不讲述原理 1 获取用户ID和cookie 用户ID在进入个人主页时导航栏中就会有显示 xff0c 例如下面这样 xff1a cookie获取 xff08 有的代码无需cookie也能运行 xff09
  • 中介变量、调节变量与协变量

    在平时看论文过程中偶会接触到这几个概念 xff0c 然而都没想过弄明白 xff0c 每次总觉得只要看明白个大概反正自己又不用这种方法 作为科研人 xff0c 还是应该保持谦逊 xff0c 保持学习 一 中介变量 1 概念 中介变量 xff0
  • linux环境搭建

    文章目录 linux环境搭建基础工具环境docker镜像docker 的基本操作git amp amp sshbash脚本 python 环境 linux环境搭建 基础工具环境 docker镜像 Docker CE 镜像源站 docker如
  • 【Linux】状态机

    Linux 状态机 首先感谢阅读 xff0c 作者是在工作中学习与积累 xff0c 每一个笔记都是心得和积累 xff0c 希望可以和大家一起交流学习 学习咱们先定好计划 xff0c 需要了解的都一样 xff0c 有 xff1a xff08
  • MyBatisPlus源码

    MyBatisPlus源码 文章目录 MyBatisPlus源码1 通用Mapper 96 BaseMapper 96 2 顶级Mapper 96 Mapper 96 3 通用Service 96 IService 96 4 通用Servi
  • bomb二进制炸弹拆除实验(MIPS)

    上学期计算机系统基础课程的一个实验 xff0c 在这里再简略整理一下 xff1a 实验要求 xff1a 仅给定一个MIPS二进制可执行文件bomb xff0c 要求运用GDB调试工具 xff0c 通过分析反汇编代码来输入正确的参数以拆除炸弹
  • 图与网络可视化实战(matlab实现)

    本篇以2020年数学建模美赛D题的足球传球网络可视化为例 xff0c 分三大步骤来简单讲解一下matlab在图与网络可视化方面的应用 xff1a Step1 构图 xff1a 首先根据输入数据构造邻接矩阵 xff0c 然后调用matlab中
  • Week8 - 程序设计思维与实践 - HRZ的序列(第二次CSP模拟T1)

    T1 HRZ的序列 该比赛已结束 xff0c 您无法在比赛模式下递交该题目 您可以点击 在题库中打开 以普通模式查看和递交本题 题目描述 相较于咕咕东 xff0c 瑞神是个起早贪黑的好孩子 xff0c 今天早上瑞神起得很早 xff0c 刷B
  • Week8 - 程序设计思维与实践 - HRZ学英语(第二次CSP模拟T2)

    T2 HRZ学英语 该比赛已结束 xff0c 您无法在比赛模式下递交该题目 您可以点击 在题库中打开 以普通模式查看和递交本题 题目描述 瑞神今年大三了 xff0c 他在寒假学会了英文的26个字母 xff0c 所以他很兴奋 xff01 于是
  • Week14 - 程序设计思维与实践 - 矩阵快速幂(+优化DP)

    题目连接 A Q老师与石头剪刀布 xff08 必做 xff09 题意 每一个大人曾经都是一个小孩 xff0c Q老师 也一样 为了回忆童年 xff0c Q老师 和 Monika 玩起了石头剪刀布的游戏 xff0c 游戏一共 n 轮 无所不知
  • Week14 - 程序设计思维与实践 - HDU3700 - Cat(限时模拟)

    Problem Description There is a cat cat likes to sleep If he sleeps he sleeps continuously no less than A hours For some
  • SDU《程序设计思维与实践》课程个人感想

    这门课到此也就宣告结束了呀 xff0c 回想过去居家学习的 16 周真的是既辛酸又充实 由于是非竞赛生 xff0c 之前没怎么接触过算法刷题这一方面 xff0c 唯一学的点算法也是来自上学期的数据结构 xff0c 回想刚开始上这门课的时候
  • 跳表的实现、分析以及可视化(Qt C++、Graphviz)

    演示程序截图 实现功能 跳表初始化跳表的查找操作跳表的插入操作跳表的删除操作 xff08 指定关键字的元素 关键字最小的元素 关键字最大的元素 xff09 跳表可视化 程序结构设计 初始化模块 xff1a void Initializati
  • 1、stm32F103入门学习--开发环境搭建(一)

    stm32简介 这款是网上销量比较多的板子 xff0c 我找板子主要还是看价格 xff0c 20元左右 xff0c 不需要太贵 xff0c 只是做个核心入门 xff0c 不要太多的外设 xff0c 特别是对初学者 xff0c 因为从来没见过
  • 森林与二叉树之间的转换以及可视化演示(C++实现)

    演示程序截图 基本要求 构造并实现森林的ADT和二叉树ADT xff0c 森林ADT中应包括初始化 插入元素 删除元素 xff0c 插入边 xff0c 删除边 xff0c 转换成二叉树 xff0c 显示森林及对应的二叉树等基本操作 xff0

随机推荐