Angular 11如何基于对象数组动态创建表单组实现动态表格行编辑
Angular 无依赖动态数据表行内编辑实现方案
核心实现原则
- 完全基于后端返回的对象数组动态生成表单结构,无硬编码字段配置
- 每行维护独立的编辑状态、FormGroup实例,行之间状态完全隔离
- 自动备份原始数据,支持取消编辑时一键回滚
- 提交时直接读取表单有效值对接后端接口,无需额外字段映射
分步改造实现
1. 行组件TypeScript逻辑补全&改造
修复原有代码缺失的属性、方法,新增动态FormGroup生成逻辑:
import { Component, Input, Output, EventEmitter, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; import { EditButtonGroupComponent } from '../../edit-button-group/edit-button-group.component'; @Component({ selector: '[dataTableRow]', templateUrl: './data-table-row-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class DataTableRowFormComponent implements OnInit { @ViewChild('editButtonGroup', { static: true }) editButtonGroup: EditButtonGroupComponent; @Input() data: any[]; @Input() useEditButton: boolean; @Input() updating: boolean; @Input() deleting: boolean; @Input() index: number; // 行级状态 editModeEnabled = false; isEditing = false; isDeleting = false; rowForm: FormGroup; private originalData: Record<string, any>; constructor( private fb: FormBuilder, private cdr: ChangeDetectorRef ) {} ngOnInit() { // 初始化时基于当前行数据动态生成FormGroup this.initRowForm(); } // 动态生成表单:遍历当前行所有key,自动创建对应FormControl private initRowForm() { this.originalData = { ...this.data[this.index] }; const group: Record<string, FormControl> = {}; Object.keys(this.originalData).forEach(key => { group[key] = this.fb.control(this.originalData[key]); }); this.rowForm = this.fb.group(group); // 初始状态禁用表单,仅查看 this.rowForm.disable(); } checkMark(value: any) { return value === true ? 'bi bi-check-lg' : ''; } booleanCheck(val: any) { return typeof val === 'boolean'; } returnZero() { return 0; } enableEditMode() { this.editModeEnabled = true; this.isEditing = true; // 启用表单控件可编辑 this.rowForm.enable(); this.cdr.markForCheck(); } disableEditMode() { this.editModeEnabled = false; this.isEditing = false; // 回滚到原始数据,禁用表单 this.rowForm.reset(this.originalData); this.rowForm.disable(); this.cdr.markForCheck(); } // 提交修改 update() { if (this.rowForm.invalid) return; const updatedData = { ...this.originalData, ...this.rowForm.getRawValue() }; this.updateSubmit.emit({ index: this.index, data: updatedData }); // 接口提交成功后调用disableEditMode()即可退出编辑态 } // 删除行 delete() { this.deleteSubmit.emit({ index: this.index, data: this.originalData }); } @Output() updateSubmit = new EventEmitter<{index: number, data: any}>(); @Output() deleteSubmit = new EventEmitter<{index: number, data: any}>(); }
2. 行组件模板改造
修复原有模板混用ngModel、绑定逻辑错误、重复事件问题,移除无用的AngularJS依赖引入:
<td *ngFor="let item of data[index] | keyvalue : returnZero"> <!-- 查看态 --> <ng-container *ngIf="!editModeEnabled"> <i [ngClass]="checkMark(item.value)" *ngIf="booleanCheck(item.value)"></i> <span *ngIf="!booleanCheck(item.value)">{{item.value}}</span> </ng-container> <!-- 编辑态 --> <ng-container *ngIf="editModeEnabled" [formGroup]="rowForm"> <!-- 布尔类型渲染复选框 --> <input type="checkbox" class="form-check-input" *ngIf="booleanCheck(item.value)" [formControlName]="item.key" > <!-- 其他类型默认渲染文本输入框,可按需扩展下拉、日期等控件类型 --> <input type="text" class="form-control" *ngIf="!booleanCheck(item.value)" [formControlName]="item.key" > </ng-container> </td> <td *ngIf="useEditButton"> <edit-button-group [editing]="isEditing && updating" [deleting]="isDeleting && deleting" (onDelete)="delete()" (onEdit)="update()" (onEditModeEnabled)="enableEditMode()" (onEditModeDisabled)="disableEditMode()" > </edit-button-group> </td>
3. 父表格层对接
父组件不需要提前定义列配置,直接接收后端返回的对象数组传入行组件即可,监听行组件抛出的更新、删除事件调用对应后端接口:
<table class="table"> <tbody> <tr dataTableRow *ngFor="let row of tableData; let i = index" [data]="tableData" [index]="i" [useEditButton]="true" [updating]="updateLoading" [deleting]="deleteLoading" (updateSubmit)="handleUpdate($event)" (deleteSubmit)="handleDelete($event)" ></tr> </tbody> </table>
扩展说明
- 若需要支持不同字段类型渲染对应编辑控件(如下拉选择、日期选择器、数字输入框),只需新增字段类型配置传入,或根据值类型自动判断,在模板里加对应
*ngIf分支即可,无需改动核心表单生成逻辑 - 表单校验可在
initRowForm方法里动态添加,根据字段名配置对应校验规则,不需要硬编码列结构 - 行级独立FormGroup的设计支持多行同时进入编辑态,不会互相干扰
内容的提问来源于stack exchange,提问作者nickiti
相关产品推荐
相关产品推荐

