响应式表单嵌套结构

在这里插入图片描述

效果

在这里插入图片描述

在这里插入图片描述

一层表单动态增加

二层表单嵌套实现(一层数组)

form-nested.component.html

<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>
    <button nz-button (click)="addStage()">增加stage</button>

    <nz-form-control nzSpan="18">
      <div class="workFlowContent" formArrayName="workFlowContent" *ngFor="let content of 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-form-control nzOffset="15" nzSpan="3" nz-col>
              <button nz-button (click)="removeStage(workflowIndex)">
                删除
              </button>
            </nz-form-control>
          </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 stageContent.controls;let stageIndex = index">
                  <input nz-input [formControlName]="stageIndex.toString()">
                </nz-row>
              </nz-form-control>
            </nz-row> -->
        </div>
      </div>
    </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>

form-nested.component.css

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

form-nested.component.ts

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

@Component({
  selector: 'uv-form-nested',
  templateUrl: './form-nested.component.html',
  styleUrls: ['./form-nested.component.css']
})
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.group({
          stageName: [null, [Validators.required]],
          stageType: [null, [Validators.required]],
          // stageContent: this.fb.array([
          //   this.fb.control(null)
          // ]),
        })
      ])
    });
  }

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

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

  ngOnInit() {
  }

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

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

提示: 要继续嵌套,在初始化时,不要设置为null,设置为this.fb.array([]),即可,否则会报错

参考

其他

三层表单嵌套实现(二层数组)

数据格式

  autoFormData: T[] = [
    {
      "key": "title",
      "value": "1-1数据",
      "ui_type": "editbox",
      "ui_title": "1-1"
    },
    {
      "key": "fireTime",
      "value": [
        {
          "key": "fireTime1",
          "value": "2-1数据",
          "ui_type": "editbox",
          "ui_title": "2-1"
        },
        {
          "key": "fireTime2",
          "value": "2-2数据",
          "ui_type": "editbox",
          "ui_title": "2-2"
        }
      ],
      "ui_type": "editboxes",
      "ui_title": "1-2"
    }
  ]

表单结构

    this.validateForm = this.fb.group({
      target: [null, [Validators.required]],
      opcode: [null, [Validators.required]],
      body: this.fb.array([
        // 初始化后的形式:
        this.fb.group({
          key: [null, [Validators.required]],
          ui_title: [null, [Validators.required]],
          ui_type: [null, [Validators.required]],
          value: this.fb.array([
            this.fb.group({
              key: [null, [Validators.required]],
              ui_type: [null, [Validators.required]],
              ui_title: [null, [Validators.required]],
              value: [null, [Validators.required]],
            })
          ]),
        })
      ])
    });

效果
在这里插入图片描述

源码

.html

<div>
  <button (click)="showFormValue()">查看表单值</button>
  <!-- <nz-switch [(ngModel)]="switchValue" (ngModelChange)="change('')"></nz-switch> -->
</div>


<nz-divider></nz-divider>

<form [formGroup]="validateForm">

  <div nz-row [nzGutter]="8">
    <div nz-col nzSpan="12">
      <nz-form-item>
        <nz-form-label nz-col nzSpan="3">target</nz-form-label>
        <nz-form-control nz-col nzSpan="21">
          <input nz-input type="text" formControlName="target">
        </nz-form-control>
      </nz-form-item>
    </div>
  </div>

  <div nz-row [nzGutter]="8">
    <div nz-col nzSpan="12">
      <nz-form-item>
        <nz-form-label nz-col nzSpan="3">opcode</nz-form-label>
        <nz-form-control nz-col nzSpan="21">
          <input nz-input type="text" formControlName="opcode">
          <!-- <nz-select formControlName="opcode" nzPlaceHolder="请选择" (ngModelChange)="opCodeChange()">
              <ng-container *ngFor="let item of opCodeOps">
                <nz-option [nzValue]="item.value" [nzLabel]="item.label"></nz-option>
              </ng-container>
            </nz-select> -->
        </nz-form-control>
      </nz-form-item>
    </div>
  </div>


  <div nz-row [nzGutter]="8">
    <div nz-col nzSpan="24">
      <nz-form-item>
        <nz-form-label nz-col nzSpan="1">body</nz-form-label>
        <nz-row nz-col nzSpan="23">
          <button (click)="newValueStr()">增加editbox</button>
          <button (click)="newValueArr()">增加editboxes</button>
        </nz-row>
      </nz-form-item>
    </div>

    <div nz-col nzSpan="24">
      <nz-form-item>
        <nz-form-control nzOffset="4" nzSpan="20">
          <div class="workFlowContent" formArrayName="body" *ngFor="let content of body.controls;
                        let workflowIndex = index">

            <div [formGroupName]="workflowIndex.toString()">
              <!-- <nz-row>formGroupName: {{workflowIndex}}</nz-row> -->
              <nz-row nz-col nzOffset="22">
                <button nz-button (click)="deleteValue(workflowIndex)">删除</button>
              </nz-row>

              <nz-row>
                <nz-form-label nzSpan="4" nz-col>ui_title</nz-form-label>
                <nz-form-control nz-col nzSpan="7">
                  <input nz-input type="text" formControlName="ui_title">
                </nz-form-control>
              </nz-row>

              <nz-row>
                <nz-form-label nzSpan="4" nz-col>ui_type</nz-form-label>
                <nz-form-control nz-col nzSpan="7">
                  <input nz-input type="text" formControlName="ui_type">
                </nz-form-control>
              </nz-row>

              <nz-row>
                <nz-form-label nzSpan="4" nz-col>key</nz-form-label>
                <nz-form-control nz-col nzSpan="7">
                  <input nz-input type="text" formControlName="key">
                </nz-form-control>
              </nz-row>


              <!-- value为字符串 -->
              <ng-container *ngIf="getValue(content).controls == undefined; else other_ediboxes">
                <nz-row>
                  <nz-form-label nzSpan="4">
                    value
                  </nz-form-label>
                  <nz-form-control nz-col nzSpan="7">
                    <input nz-input type="text" formControlName="value">
                  </nz-form-control>
                </nz-row>
              </ng-container>

              <!-- value为数组 -->

              <ng-template #other_ediboxes>
                <nz-row>
                  <nz-form-label nz-col nzSpan="4">
                    value
                  </nz-form-label>

                  <nz-row nz-col nzSpan="2">
                    <button (click)="newSubValueStr(workflowIndex)">增加</button>
                  </nz-row>
                </nz-row>
                <nz-row>


                  <nz-form-control nzOffset="4" nz-col nzSpan="20">
                    <div formArrayName="value" class="stageContent" *ngFor="let stage of getValue(content).controls;
                                        let stageIndex = index">

                      <div [formGroupName]="stageIndex.toString()">
                        <!-- <nz-row>formGroupName: {{stageIndex}}</nz-row> -->
                        <!-- {{vJson(stage)}} -->
                        <!-- {{getControlsValueByKey(stage, "value")}} -->

                        <nz-row nz-col nzOffset="22" style="margin-bottom: 10px;">
                          <button (click)="deleteSubValue(workflowIndex, stageIndex)">删除</button>
                        </nz-row>

                        <nz-row>
                          <nz-form-label nzSpan="8" nz-col>ui_title</nz-form-label>
                          <nz-form-control nzSpan="16" nz-col>
                            <input nz-input type="text" formControlName="ui_title" placeholder="请输入">
                          </nz-form-control>
                        </nz-row>

                        <nz-row>
                          <nz-form-label nzSpan="8" nz-col>ui_type</nz-form-label>
                          <nz-form-control nzSpan="16" nz-col>
                            <input nz-input type="text" formControlName="ui_type" placeholder="请输入">
                          </nz-form-control>
                        </nz-row>

                        <nz-row>
                          <nz-form-label nzSpan="8" nz-col>key</nz-form-label>
                          <nz-form-control nzSpan="16" nz-col>
                            <input nz-input type="text" formControlName="key" placeholder="请输入">
                          </nz-form-control>
                        </nz-row>

                        <nz-row>
                          <nz-form-label nzSpan="8" nz-col>value</nz-form-label>
                          <nz-form-control nzSpan="16" nz-col>
                            <ng-container>
                              <input nz-input type="text" formControlName="value" placeholder="请输入">
                            </ng-container>
                          </nz-form-control>
                        </nz-row>

                      </div>
                    </div>
                  </nz-form-control>
                </nz-row>
                <nz-row>
                </nz-row>
              </ng-template>
            </div>
          </div>
        </nz-form-control>
      </nz-form-item>
    </div>
  </div>
</form>

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

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

<div nz-row [nzGutter]="10">
  <div nz-col nzSpan="12">
    <button nz-button nzType="primary" (click)="submit()">提交</button>
  </div>
</div>

.ts

import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UhttpService } from '@uviews/conn-server';
import { NzMessageService } from 'ng-zorro-antd/message';
import { ShareService } from '../service/share.service';
import { HttpClient } from '@angular/common/http';

interface T {
  key: string,
  value: any,
  ui_type: string,
  ui_title: string
}

@Component({
  selector: 'uv-add-conf2',
  templateUrl: './add-conf2.component.html',
  styleUrls: ['./add-conf2.component.css']
})
export class AddConf2Component implements OnInit {

  vJson(e: any) {
    // console.log(e)
    // console.log(e.controls)
    // console.log(e.controls["value"].value)
    console.log(JSON.stringify(e, undefined, 2))
  }

  // 根据key获取FormControls的值
  getControlsValueByKey(e: any, key: string) {
    // console.log(e.controls[key].value)
    return e.controls[key].value
  }

  public validateForm!: FormGroup;

  constructor(
    public shareService: ShareService,
    private fb: FormBuilder,
    private message: NzMessageService,
    private uHttp: UhttpService,
    private http: HttpClient
    ) {

  }

  ngOnInit() {
    this.initFormGroup()
  }

  // 初始化基础表单
  initFormGroup() {
    this.validateForm = this.fb.group({
      target: [null, [Validators.required]],
      opcode: [null, [Validators.required]],
      body: this.fb.array([
        // 初始化后的形式:
        // this.fb.group({
        //   key: [null, [Validators.required]],
        //   ui_title: [null, [Validators.required]],
        //   ui_type: [null, [Validators.required]],
        //   value: this.fb.array([
        //     this.fb.group({
        //       key: [null, [Validators.required]],
        //       ui_type: [null, [Validators.required]],
        //       ui_title: [null, [Validators.required]],
        //       value: [null, [Validators.required]],
        //     })
        //   ]),
        // })
      ])
    });
  }

  // 获取body的formControls
  get body(): FormArray {
    return this.validateForm.get('body') as FormArray;
  }

  // 获取gcontent对应的value的formControls
  getValue(content: any) {
    return content.get('value') as FormArray;
  }

  // 新建FormControl类型的value,即value为字符串
  newValueStr() {
    this.body.push(
      this.fb.group({
        key: ['', [Validators.required]],
        ui_type: ['', [Validators.required]],
        ui_title: ['', [Validators.required]],
        value: ['', [Validators.required]]
      })
    )
  }

  // 新建FormArray类型的value,即value为数组
  newValueArr() {
    this.body.push(
      this.fb.group({
        key: ['', [Validators.required]],
        ui_type: ['', [Validators.required]],
        ui_title: ['', [Validators.required]],
        value: this.fb.array([
          this.fb.group({
            key: ['', [Validators.required]],
            ui_type: ['', [Validators.required]],
            ui_title: ['', [Validators.required]],
            value: ['', [Validators.required]]
          })
        ]),
      })
    );
  }

  // 删除第一层value
  deleteValue(index: number) {
    console.log(index)
    this.body.removeAt(index);
  }

  // 新建第二层FormControl类型的value,即value为字符串
  newSubValueStr(bodyIndex: number) {
    (this.body.at(bodyIndex).get('value') as FormArray).push(
      this.fb.group({
        key: ['', [Validators.required]],
        ui_type: ['', [Validators.required]],
        ui_title: ['', [Validators.required]],
        value: ['', [Validators.required]]
      })
      );
  }

  // 删除第二层value
  deleteSubValue(valueIndex: number, subValueIndex: number) {
    (this.body.at(valueIndex).get('value') as FormArray).removeAt(subValueIndex);
  }

  showFormValue() {
    console.log(this.validateForm)
    console.log(JSON.stringify(this.validateForm.value, undefined, 2))
  }

  submit() {
    this.vJson(this.validateForm.value)
    if (this.validateForm.valid) { 
      const data = this.validateForm.value
      this.http.patch(`http://127.0.0.1:8000/api/control_console/${data.target}/${data.opcode}`, data.body).subscribe(
        (res: any) => {
          if (res.code == 0) {
            this.message.success("添加成功")
            this.validateForm.reset()
          } else {
            this.message.error(res.message)
          }
        }
      )

    }
    else {
      Object.values(this.validateForm.controls).forEach(control => {
        console.log(control)
        if (control.invalid) {
          control.markAsDirty();
          control.updateValueAndValidity({ onlySelf: true });
        }
        if(control.value != null) {
          // console.log(control.valid)
          this.message.error("请填写完整")
        }
      });
    }


  }

}

参考