如何在Angular的Ag-Grid中实现行增删的撤销/重做功能?
解决方案:扩展Ag-Grid实现行增删的撤销/重做
核心思路
Ag-Grid原生撤销重做仅覆盖单元格编辑、剪贴板操作,行增删需要手动维护操作历史栈,记录每一次行操作的关键信息(操作类型、行数据、行标识/索引),再通过自定义方法触发数据回滚。
具体实现步骤
1. 定义操作历史数据结构
在组件类中声明栈结构与操作类型,用于存储行操作快照:
// 行操作类型枚举 enum RowOperationType { INSERT = 'insert', DELETE = 'delete' } // 操作历史项接口 interface RowOperationHistory { type: RowOperationType; rowData: any; // 操作的行数据(需包含唯一标识如id) rowPosition: number; // 操作时的行插入/所在索引 } // 维护撤销、重做两个栈 private undoStack: RowOperationHistory[] = []; private redoStack: RowOperationHistory[] = [];
2. 封装带历史记录的行增删方法
不要直接调用Ag-Grid原生的updateRowData,改用自定义方法记录操作:
新增行方法
addRow(newRow: any) { const insertIndex = this.gridApi.getDisplayedRowCount(); // 执行新增 this.gridApi.updateRowData({ add: [newRow], addIndex: insertIndex }); // 记录到撤销栈,同时清空重做栈 this.undoStack.push({ type: RowOperationType.INSERT, rowData: newRow, rowPosition: insertIndex }); this.redoStack = []; }
删除行方法
deleteSelectedRow() { const selectedRows = this.gridApi.getSelectedRows(); if (selectedRows.length === 0) return; const targetRow = selectedRows[0]; // 获取行的原始索引(若有排序/过滤,建议用rowNode.id替代可视化索引) const rowNode = this.gridApi.getRowNode(targetRow.id); const deleteIndex = rowNode.rowIndex; // 执行删除 this.gridApi.updateRowData({ remove: [targetRow] }); // 记录到撤销栈,清空重做栈 this.undoStack.push({ type: RowOperationType.DELETE, rowData: targetRow, rowPosition: deleteIndex }); this.redoStack = []; }
3. 实现撤销与重做逻辑
撤销操作
undoRowOp() { if (this.undoStack.length === 0) return; const lastOp = this.undoStack.pop(); if (!lastOp) return; switch (lastOp.type) { case RowOperationType.INSERT: // 撤销新增:删除对应行 this.gridApi.updateRowData({ remove: [lastOp.rowData] }); this.redoStack.push(lastOp); break; case RowOperationType.DELETE: // 撤销删除:在原位置插入该行 this.gridApi.updateRowData({ add: [lastOp.rowData], addIndex: lastOp.rowPosition }); this.redoStack.push(lastOp); break; } }
重做操作
redoRowOp() { if (this.redoStack.length === 0) return; const lastRedoOp = this.redoStack.pop(); if (!lastRedoOp) return; switch (lastRedoOp.type) { case RowOperationType.INSERT: // 重做新增:再次插入该行 this.gridApi.updateRowData({ add: [lastRedoOp.rowData], addIndex: lastRedoOp.rowPosition }); this.undoStack.push(lastRedoOp); break; case RowOperationType.DELETE: // 重做删除:再次删除该行 this.gridApi.updateRowData({ remove: [lastRedoOp.rowData] }); this.undoStack.push(lastRedoOp); break; } }
4. 模板绑定与原生功能联动
在component.html中绑定自定义按钮,并保留原生撤销重做配置:
<!-- 自定义行操作撤销/重做按钮 --> <button (click)="undoRowOp()" [disabled]="undoStack.length === 0">撤销行操作</button> <button (click)="redoRowOp()" [disabled]="redoStack.length === 0">重做行操作</button> <ag-grid-angular style="width: 100%; height: 500px;" class="ag-theme-alpine" [rowData]="rowData" [columnDefs]="columnDefs" [undoRedoCellEditing]="true" [undoRedoClipboard]="true" (gridReady)="onGridReady($event)" ></ag-grid-angular>
若需要和原生单元格编辑撤销重做联动,可监听原生栈变化清空自定义栈:
onGridReady(params: GridReadyEvent) { this.gridApi = params.api; // 原生撤销栈变化时,清空自定义行操作栈避免冲突 this.gridApi.addEventListener('undoRedoStackChanged', () => { this.undoStack = []; this.redoStack = []; }); }
注意事项
- 行数据必须包含唯一标识(如
id),确保增删操作能精准匹配目标行 - 若使用排序、过滤功能,建议用
rowNode.id替代可视化rowIndex,避免索引偏移导致回滚位置错误 - 批量行操作需调整历史记录结构,支持存储批量数据与批量回滚
内容的提问来源于stack exchange,提问作者Donald Hunter Kengue
相关产品推荐
相关产品推荐

