Angular如何实现单按钮获取并提交多个ngForm表单值
问题核心
你当前通过*ngFor循环生成了多个独立的模板驱动表单,每个表单的模板引用变量属于循环局部作用域,外部无法统一访问所有表单实例,自然无法一次性聚合所有提交数据。将提交按钮移到表单外部会脱离表单原生的submit关联逻辑,单独使用ngModel没有做统一值收集也无法生效。另外现有代码存在控件重名bug:分类类型的mat-select写死了name="option",同个表单内多个控件重名会导致ngModel值互相覆盖。
实现方案
方案1:批量获取循环生成的表单实例(改动最小,保留单表单独立校验能力)
不需要大幅调整现有模板结构,仅需做3处修改:
- 给循环生成的表单加统一的模板引用标识
- 移除每个表单内部的提交按钮,在所有表单外部放置统一提交按钮,绑定单独的批量提交方法
- 组件类中通过
@ViewChildren获取所有表单实例,提交时遍历聚合所有表单值,同时可做单表单独立校验
模板修改
<!-- 循环生成的表单增加统一模板引用名 --> <form #modelForm="ngForm" *ngFor="let x of selectedValue; index as formIdx"> <mat-card> <mat-card-title class="title-card"> <span>{{x.name}}</span> </mat-card-title> <mat-card-content *ngFor="let param of x.modelParameter; index as paramIdx"> <div class="param-title"> <span><b>{{param.name}}</b></span> </div> <div class="set-slider" *ngIf="param.type === 'continuous'"> <label class="gridSizeValue"><b>{{param.values_arr[0]}}</b></label> <mat-slider (change)="updateSetting($event)" class="slider" thumbLabel tickInterval="1" [displayWith]="formatLabel" min="{{param.values_arr[0]}}" max="{{param.values_arr[1]}}" aria-label="units" class="form-control" ngModel name="{{param.name}}"> </mat-slider> <label class="gridSizeValue"><b>{{param.values_arr[1]}}</b></label> </div> <div class="set-categorical" *ngIf="param.type === 'categorical'"> <mat-form-field appearance="outline"> <mat-select class="form-control" ngModel name="{{param.name}}"> <mat-option *ngFor="let op of param.values_arr" [value]="op"> {{op}} </mat-option> </mat-select> </mat-form-field> </div> </mat-card-content> </mat-card> </form> <!-- 所有表单外部放置统一提交按钮 --> <button mat-raised-button type="button" (click)="submitAll()" class="save-button">Send Models</button>
组件类代码
import { Component, QueryList, ViewChildren } from '@angular/core'; import { NgForm } from '@angular/forms'; @Component({ // 你自己的组件配置 }) export class YourComponent { selectedValue: any[] = []; // 原有业务数据源 // 获取所有标记为modelForm的表单实例 @ViewChildren('modelForm') allForms!: QueryList<NgForm>; submitAll() { const submitData: any[] = []; let hasInvalid = false; this.allForms.forEach((formInstance, index) => { // 校验单表单合法性 if (formInstance.invalid) { formInstance.markAllAsTouched(); hasInvalid = true; return; } submitData.push({ modelName: this.selectedValue[index].name, params: formInstance.value }) }) if (hasInvalid) return; // 此处拿到所有表单的聚合数据,可直接发起接口请求 console.log('批量提交数据', submitData); } }
方案2:单大表单包裹所有循环内容(适合无分块校验需求的场景)
如果不需要每个卡片的表单做独立校验,可以直接把循环层级放到mat-card上,外层仅保留一个表单标签,提交时直接拿单个表单实例即可获取所有控件值:
<form (ngSubmit)="onSubmit(singleForm)" #singleForm="ngForm"> <mat-card *ngFor="let x of selectedValue; index as formIdx"> <mat-card-title class="title-card"> <span>{{x.name}}</span> </mat-card-title> <mat-card-content *ngFor="let param of x.modelParameter; index as paramIdx"> <!-- 内部控件注意设置全局唯一name,比如加表单索引前缀:name="{{formIdx}}_{{param.name}}" --> <!-- 原有控件逻辑不变 --> </mat-card-content> </mat-card> <button mat-raised-button type="submit" class="save-button">Send Models</button> </form>
该方案缺点是所有控件属于同一表单,任意一个控件填写不符合规则会导致整个表单无法提交,适合表单规则统一、不需要分块校验的场景。
内容的提问来源于stack exchange,提问作者skyalmdaa1
相关产品推荐
相关产品推荐

