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
相关产品推荐
相关产品推荐

