You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 13:15:35