Angular 撤销重做历史存储 | ng-hub-ui-history

Angular 撤销和重做历史存储,提供对 Signals 友好的 API、事务、限制以及响应式表单支持。

最后更新 2026年9月6日

概览

团队为何搜索这个库

当你需要在复杂状态上实现撤销和重做,又不想引入一个完整的外部状态库时,请使用这个 Angular 历史存储。

安装

npm install ng-hub-ui-history

跳转到

适用于

  • 编辑器
  • 表单构建器
  • low-code 工具
  • 配置界面

关于 history

ng-hub-ui-history 在 Angular 编辑器、表单构建器和配置页面中尤为有用,这些场景下用户期望能安全地回退。它为有状态的工作流带来撤销和重做模式,而无需你做出沉重的架构决策。

功能指南

手动提交与时间旅行

跟踪状态变化,并在线性历史中向后/向前穿梭。

示例:
基础历史记录

对单个被跟踪对象进行手动提交、撤销与重做。

{
  "title": "Getting Started with History",
  "description": "Edit and commit to create undo/redo entries."
}

嵌套对象与数组

对深层对象图和列表变更使用基于补丁的跟踪。

示例:
嵌套对象历史记录

以补丁形式记录嵌套属性和列表更新。

{
  "id": "invoice-1",
  "customer": {
    "name": "Acme Corp",
    "city": "Madrid"
  },
  "lines": [
    {
      "name": "Support plan",
      "quantity": 1,
      "unitPrice": 150
    }
  ]
}

自动表单跟踪

借助内置的 watch 辅助器自动提交表单值的变化。

示例:
响应式表单监听

watchForm() 自动提交 FormGroup 的每一次值变化。

{
  "name": "Maria",
  "email": "maria@demo.local",
  "role": "Editor"
}

事务与保留策略

可将多个操作合并为一条记录,并按配置的限制裁剪旧记录。

示例:
事务与限制

由事务合并的提交,并受记录数与字节数限制。

Pointer: -1 / Entries: 0 / Bytes: 0

{
  "id": "transaction-editor",
  "title": "Release notes draft",
  "content": "Initial draft content.",
  "tags": [
    "draft"
  ]
}

核心特性

最近更改

Version 22.0.4 - 9/6/26, 12:00 AM

fixed: The published manifest no longer declares a main entry. It pointed at src/public-api.ts, the ng-packagr entry file, which never travels inside the tarball, so any resolver that ignores the exports map (older bundlers, legacy Jest resolution, plain require) followed it straight to a file that is not there. Dropping the field leaves exports, module and typings as the only entry points, the shape ng-packagr emits for every other library in the monorepo.

fixed: A commit that only moves a Date (or a Set, Map or RegExp) is recorded again. The default diff walked every object key by key, and those types keep their payload outside their own enumerable keys, so the comparison found nothing and produced an empty patch. An empty patch reads as no change: commit() returned false, the tracked state kept the old value that getState() and states() then handed back, and on a mixed commit undo() restored the other fields while leaving the new date in place, all without an error. Those values are now compared and replaced as a whole, which is what structuredClone already round-trips; an instance rebuilt with the same content still records no entry.

Version 22.0.3 - 9/1/26, 12:00 AM

changed: The homepage in the manifest points at this library's own documentation page rather than at the site root. It is the link a registry shows beside the package and the one a reader clicks from it, and landing on a front page they then have to search is a worse answer than landing on the reference for the package they were already looking at. Metadata only — no code, no types, no styles change, and nothing a consumer imports is affected.

Version 22.0.2 - 8/8/26, 12:00 AM

fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.

Version 22.0.1 - 7/28/26, 12:00 AM

added: Test suite for the diff/patch/clone utilities and multi-object undo/redo: cloneDeep isolation (nested structures, Dates), createDefaultDiff/applyDefaultPatch round-trips across representative shapes (nested objects, array insert/remove/reorder, key deletion, null transitions, deep nesting), estimateBytes sanity, and store-level coverage for independent per-object timelines, no-op transactions and redo invalidation after transactional commits. No runtime changes.

Version 22.0.0 - 6/17/26, 12:00 AM

changed: Aligned with Angular 22.

changed: README documentation standardized.

Version 0.1.0 - 6/17/26, 12:00 AM

added: Initial release with multi-object linear history.

added: Undo/redo, transactions, and maxEntries/maxBytes retention limits.

added: Reactive Forms auto-commit integration via watchForm().