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

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组件。

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值