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

Angular中如何基于实例更新嵌套数组对象并实现取消回滚

Angular嵌套数组编辑:实现确认/取消功能

问题分析

直接绑定原对象实例会导致修改时实时影响原数据,无法实现取消回滚。使用浅拷贝{...selected}仅能复制第一层属性,嵌套的fields仍与原对象共享引用,修改临时变量的嵌套字段仍会污染原数据。

解决方案

1. 深拷贝创建临时编辑对象

确保临时变量与原数据完全独立,修改时不影响原数据。

2. 保存原对象引用

确认编辑时,将临时变量的所有属性(包括嵌套层级)同步到原对象,保持原引用不变(避免视图不更新)。

3. 实现取消/确认逻辑

  • 取消:直接清空编辑状态,原数据未被修改,自动恢复。
  • 确认:递归同步临时对象的属性到原对象。

完整代码实现

组件逻辑

import { Component } from '@angular/core';

@Component({
  selector: 'app-nested-edit',
  templateUrl: './nested-edit.component.html'
})
export class NestedEditComponent {
  arr = [
    {
      "name": "field-1",
      "fields": [
        {
          "name": "Field-2",
          "fields": []
        },
        {
          "name": "field-3",
          "fields": [
            {
              "name": "field-3",
              "fields": []
            }
          ]
        }
      ]
    }
  ];

  data_to_edit: any;
  original_item: any;

  editThis(selected: any) {
    // 深拷贝生成临时编辑对象,完全隔离原数据
    this.data_to_edit = JSON.parse(JSON.stringify(selected));
    // 保存原对象引用,用于后续同步
    this.original_item = selected;
  }

  confirmEdit() {
    if (!this.data_to_edit || !this.original_item) return;
    // 递归同步临时对象属性到原对象,保留原引用
    this.syncObject(this.original_item, this.data_to_edit);
    // 清空编辑状态
    this.resetEditState();
  }

  cancelEdit() {
    // 直接清空编辑状态,原数据未被修改
    this.resetEditState();
  }

  private resetEditState() {
    this.data_to_edit = null;
    this.original_item = null;
  }

  // 递归同步对象属性,处理嵌套结构和数组
  private syncObject(target: any, source: any) {
    Object.keys(source).forEach(key => {
      if (typeof source[key] === 'object' && source[key] !== null) {
        if (Array.isArray(source[key])) {
          // 数组处理:清空原数组后添加新元素,保留原数组引用
          target[key] = [];
          source[key].forEach(item => target[key].push(item));
        } else {
          // 嵌套对象递归同步
          this.syncObject(target[key], source[key]);
        }
      } else {
        // 基本类型直接赋值
        target[key] = source[key];
      }
    });
  }
}

模板代码

<ng-template #recursion let-data="data">
  <ng-container *ngFor="let item of data">
    <div>
      <!-- 编辑状态显示输入框,否则显示文本 -->
      <input 
        *ngIf="original_item === item" 
        [(ngModel)]="data_to_edit.name" 
        placeholder="Enter name"
      />
      <span *ngIf="original_item !== item">{{item.name}}</span>

      <!-- 非编辑状态显示编辑按钮 -->
      <button 
        *ngIf="original_item !== item" 
        (click)="editThis(item)"
        style="margin-left: 8px;"
      >
        Edit
      </button>

      <!-- 编辑状态显示确认/取消按钮 -->
      <div *ngIf="original_item === item" style="margin-top: 4px;">
        <button (click)="confirmEdit()" style="margin-right: 8px;">Confirm</button>
        <button (click)="cancelEdit()">Cancel</button>
      </div>
    </div>

    <!-- 递归渲染嵌套字段 -->
    <div 
      *ngIf="item.fields && item.fields.length" 
      style="margin-left: 24px;"
    >
      <ng-container *ngTemplateOutlet="recursion; context:{data: item.fields}"></ng-container>
    </div>
  </ng-container>
</ng-template>

<!-- 渲染根数组 -->
<ng-container *ngTemplateOutlet="recursion; context:{data: arr}"></ng-container>

注意事项

  • 如果数据包含Date、函数或Symbol等特殊类型,JSON.parse(JSON.stringify)会丢失这些类型,此时建议使用Lodash的cloneDeep方法实现深拷贝。
  • 同步属性时必须保留原对象/数组的引用,否则Angular的变更检测无法感知到数据变化,视图不会更新。

内容的提问来源于stack exchange,提问作者saiteja

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:53:22