Angular 撤销重做历史存储 | ng-hub-ui-history
Angular 撤销和重做历史存储,提供对 Signals 友好的 API、事务、限制以及响应式表单支持。
最后更新 2026年9月20日
概览
团队为何搜索这个库
当你需要在复杂状态上实现撤销和重做,又不想引入一个完整的外部状态库时,请使用这个 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.6 - 9/20/26, 12:00 AM
changed: The npm keywords name the problem the package solves — undo-redo, time-travel, state-management, store, transactions, diff, patch, reactive-forms — which is what somebody looking for it would type. Metadata only: no code, types or styles change.
Version 22.0.5 - 9/16/26, 12:00 AM
changed: Repository, issue and README links follow the move to the hub-env organization. Issues for every Hub UI package are now gathered in hub-env/hub-ui, and the repository and bugs fields of the manifest point at the new addresses. No code, types or styles change.
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().
相关库
常见问题
Angular 应用里怎么实现撤销和重做?
createHistoryStore() 给你一个 store。调用一次 registerObject(id, initialState),每次变更调用 commit(id, nextState, { label }),然后用 undo(id) 和 redo(id),按钮状态由 canUndo(id) 和 canRedo(id) 驱动。states 是一个 signal,保存着所有被跟踪对象的当前值,模板可以直接读它。这个包里没有组件,也没有指令:那个工厂函数和它的类型就是全部的公开接口。
它能跟踪响应式表单吗?
watchForm(id, form) 会订阅 valueChanges 并替你提交每一次变更,同时返回停止它的那个函数——它不会自己清理,所以要留着这个回调,在组件销毁时调用。skipInitial 会忽略第一次发射。它提交的是 form.value 而不是 getRawValue(),所以被禁用的控件不在快照里,撤销也不会把它还原。beginTransaction 和 endTransaction 可以把一连串编辑折叠成一个撤销步骤。
它会保留多少条历史记录?
maxEntries 默认 100,maxBytes 默认 512000,都按被跟踪的对象分别计算,最旧的记录先被丢掉;history(id) 返回元数据,clearHistory(id) 从头开始。时间线是线性的,而且按对象划分:一次一步,没有分支,不能一次撤销多个对象,也不会写入任何存储,所以刷新之后是空的。
它会帮我绑定 Ctrl+Z 吗?
不会。这个包完全没有 DOM——没有键盘快捷键,没有工具栏,也没有 ARIA——所以 Ctrl+Z 要你自己绑到 undo(id) 上。还有两点常让人意外。这个 store 是一个普通的工厂函数,不是可注入的服务,所以想在多个组件之间共享,就得自己把它提供出去。另外,用一个从没注册过的 id 调用 commit 或 undo 会抛错,而不是返回 false,这是有意为之:否则 id 写错了就会悄无声息地失败。