Angular Toast 通知服务 | ng-hub-ui-toast
standalone 的 Angular toast 通知服务,支持 Signals、生命周期 observables、进度条、六个位置以及完整的 CSS 变量主题化。
最后更新 2026年9月20日
概览
团队为何搜索这个库
当你需要一个零配置的命令式通知层,既能与 Angular 的 standalone 架构共存,又能开箱即用地提供基于 Observable 的生命周期钩子时,请使用 ng-hub-ui-toast。
安装
npm install ng-hub-ui-toast跳转到
适用于
- 表单提交反馈
- 后台同步通知
- 错误警报
- 会话过期警告
关于 toast
ng-hub-ui-toast 是一个面向 Angular 22+ standalone 应用的、由 signal 驱动的 toast 服务。可从任意组件或服务中调用 success()、error()、warning() 或 info();overlay 容器会在首次触发通知时延迟挂载。每次调用都会返回一个带有 onShown、onHidden 和 onTap observables 以及 manualClose() / resetTimeout() 方法的 HubToastRef。可通过 provideToast() 全局配置默认值,并按调用覆盖任意选项。
功能指南
基本 toast 类型
四种语义类型 —— success、error、warning 和 info —— 每一种都映射到对应的设计系统颜色令牌。
示例:
基本类型
基本类型
配置选项
可按调用或通过 provideToast() 全局控制 auto-dismiss 超时、进度条、关闭按钮以及 tap-to-dismiss 行为。
示例:
配置选项
配置选项
The × is decorative, so this string is the whole name a screen reader announces for the toast's only control. The library ships it as the English "Close" and translates nothing: set it once for the application with provideToast({ closeButtonAriaLabel }), or per call as this demo does.
定位
屏幕上的六个固定位置:top/bottom 与 right、left 或 center 组合。
示例:
定位
定位
去重
preventDuplicates 会静默丢弃与已显示消息和类型相匹配的新通知。
示例:
防止重复
防止重复
Click the button multiple times. With preventDuplicates enabled, only one toast with the same message will appear.
容量控制
maxOpened 限制可见堆栈的数量;autoDismiss 决定在达到上限时是移除最旧的 toast 还是静默丢弃新的。
示例:
最大打开数与 autoDismiss
最大打开数与 autoDismiss
When autoDismiss is false, new toasts are silently dropped once the cap is reached. When true, the oldest toast is removed to make room.
生命周期 observables
每个 HubToastRef 都暴露 onShown、onHidden 和 onTap observables,以及用于命令式控制的 manualClose() 和 resetTimeout()。
示例:
生命周期 observables
生命周期 observables
CSS 变量主题化
每一处视觉细节 —— 背景、圆角、阴影、内边距、进度条 —— 都是一个 --hub-toast-* 自定义属性,可从任意祖先元素或样式表中覆盖。
示例:
CSS 变量主题化
CSS 变量主题化
Active token overrides (applied to this demo's container):
--hub-toast-border-radius: 0.75rem;
--hub-toast-shadow: 0 4px 20px rgba(0,0,0,.18);
--hub-toast-padding-x: 1.25rem;
--hub-toast-padding-y: 1rem;
--hub-toast-font-size: 0.9rem; Tokens are injected from the page's stylesheet or from a style binding on any ancestor element — the container reads them via CSS inheritance.
核心特性
最近更改
Version 22.11.2 - 9/20/26, 12:00 AM
changed: The npm keywords name the words people actually search with — toasts, notifications, snackbar, alert, message, popup, overlay — plus accessibility and a11y. Metadata only: no code, types or styles change.
Version 22.11.1 - 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.11.0 - 9/8/26, 12:00 AM
changed: BREAKING — the four exported classes are renamed with the Hub prefix: ToastService is now HubToastService, ToastConfigService is HubToastConfigService, ToastComponent is HubToastComponent and ToastContainerComponent is HubToastContainerComponent. ToastService is the name an application is most likely to give its own notification wrapper, so a library holding it holds something that was never the library’s to hold. The types of this same package — HubToastConfig, HubToastRef, HubToastPosition — already carried the prefix, so the surface was being spelled two ways in one import list. All four old names stay exported as deprecated aliases resolving to the same classes, so injection and providers keep working unchanged, and they are removed in 23.0.0. See BREAKING_CHANGES.md.
changed: ng-hub-ui-ds is declared as an optional peer dependency, >=22.0.0. The stylesheet themes against --hub-sys-*, and the manifest never said so: nothing warned that a ng-hub-ui-ds older than the --hub-ref-* / --hub-sys-* architecture would leave the toast on its fallbacks, and a reader of the manifest had no way to learn that the token package is what turns the theme on.
fixed: A toast now keeps its colour when ng-hub-ui-ds is not installed. The nine accent declarations, one per built-in type plus the danger mapping that error uses, read --hub-sys-color-<type> with no fallback, and they were the only --hub-sys-* reads in the sheet without one. Without the token package the read has nothing to resolve to, which makes the whole --hub-toast-accent declaration invalid at computed-value time rather than falling back to the neutral accent declared above it; --hub-toast-bg, --hub-toast-color and --hub-toast-border are all mixed from that slot, so background, color and border-color went unset together and a success toast arrived transparent and unbordered. Each type now names the Bootstrap-equivalent colour the rest of this family already uses as its fallback.
Version 22.10.0 - 9/7/26, 12:00 AM
fixed: A notification dropped at maxOpened no longer hands back a handle that never says anything. With the stack full and autoDismiss off the call is refused, and the handle returned was built over an empty object: its three lifecycle observables were undefined and each fell back to a brand-new Subject that nothing would ever complete or emit. Awaiting the close of that notification waited for the rest of the session, and nothing on the handle said the notification had not even been shown. The handle now closes at once: onHidden emits and completes, onShown and onTap complete without emitting, and manualClose() and resetTimeout() are no-ops.
added: HubToastRef.dropped says whether the call opened a notification at all: true when it was refused because the stack was full, false on every handle that stands for a real toast. toastId stays -1 on a dropped handle, but that was never documented and reads as an implementation detail.
changed: BREAKING (types) — HubToastRef gained a required dropped boolean. Only code that builds a HubToastRef by hand is affected, a test double or a fake service; everything that takes the handle from ToastService keeps compiling. See BREAKING_CHANGES.md.
fixed: A new notification no longer relocates the ones already on screen. There was a single container, and it took its position class from whichever toast happened to be first in the list, so a call with a different positionClass dragged every visible toast to the new corner, including the ones the user was in the middle of reading. Each position now has its own container, mounted the first time a toast asks for it, and a toast is only ever rendered by the container of its own corner. It is the arrangement ngx-toastr and react-toastify have used for years: one overlay per position, created on demand.
fixed: A toast that has just opened is painted above the ones already there. With newestOnTop the newest toast is rendered first in the DOM, and a first sibling paints underneath the ones that follow it, so an arriving toast slid in beneath the shadows of its neighbours. Stacking now follows recency rather than DOM order.
changed: BREAKING (markup) — the overlay is one container per position, not one container. Where document.body used to hold a single hub-toast-container whose class changed as toasts came and went, it now holds one element per position class actually used, each keeping its own corner for good. CSS or tests written around there being exactly one container, or around its class changing, need updating. See BREAKING_CHANGES.md.
added: ToastContainerComponent takes a position input, the position class it owns and the only one whose toasts it renders. ToastService sets it on mount; it defaults to toast-top-right.
added: The documentation says what maxOpened counts. The cap is on the whole stack, not on one corner of it, and autoDismiss therefore drops the oldest toast on screen even when that toast is in a different position. Behaviour is unchanged: it was simply impossible for a consumer to know which of the two it was.
Version 22.8.0 - 9/6/26, 12:00 AM
added: The accessible name of the close button is now a configuration option, closeButtonAriaLabel. The close glyph is decorative, so that name was the only thing a screen reader had to announce the single control of a toast, and it was the English literal Close, frozen in the template and unreachable from provideToast() or a per-call config. A library shipped to applications that are not in English cannot hardcode a user-facing string.
added: A FUNCTIONALITIES.md file. Nine sibling libraries carry one and the repository checklist asks for it on every change; toast had none, so nothing recorded which parts of the API a reader of the docs can actually operate and which are only prose.
changed: HubToastConfig now carries a required closeButtonAriaLabel. Only code that builds a full HubToastConfig literal by hand is affected: everything that goes through provideToast() or a per-call override keeps compiling. See BREAKING_CHANGES.md.
changed: HubToastData now carries a required restartToken signal. The data object keeps its identity for the whole life of a toast, so nothing in it could tell the rendered component to start its countdown over, and this signal is that channel. Only code that builds a HubToastData literal by hand is affected, as BREAKING_CHANGES.md explains.
fixed: A closing toast now completes its three lifecycle observables, not just onHidden. onShown and onTap were left open on a toast that no longer existed, so a consumer who subscribed without a takeUntil kept the subscription, and the toast data behind it, alive for the rest of the session, with no completion to hang a teardown on. Both remove() and clear() now close the whole lifecycle.
fixed: The documentation no longer describes a library that does not exist. README.md and README.es.md sold the package as having zero external dependencies and listed only the two Angular peers, so anyone installing by hand rather than through ng add ng-hub-ui got a module-not-found on resolveHubAccent the first time a toast fired. They also gave --hub-toast-title-font-weight as a bare 600 when the code routes it through --hub-ref-font-weight-semibold, typed positionClass as HubToastPosition when it accepts any string, named four semantic types where the stylesheet maps nine, omitted the three derived accent roles, and documented two of the eight public exports. BREAKING_CHANGES.md was also missing the 22.3.0 rename of --hub-toast-container-z-index to --hub-toast-container-zindex, the one kind of change that file exists to announce, because a renamed custom property fails silently.
fixed: HubToastRef.resetTimeout() restarts the auto-dismiss countdown, as its documentation always promised. It used to re-emit onShown$ and touch no timer state at all, so a caller who wanted to keep a toast on screen a while longer had no way to do it: the toast still vanished on its original schedule, and the spurious emission also broke the fires-once contract of onShown. A toast configured with disableTimeOut stays persistent, as before.
Version 22.7.2 - 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. Metadata only — nothing a consumer imports is affected.
Version 22.7.1 - 8/17/26, 12:00 AM
fixed: The published package declared no licence. An absent license field reads legally as all rights reserved; the intent was always MIT, now stated in package.json and shipped in a LICENSE file.
Version 22.7.0 - 8/14/26, 12:00 AM
changed: Replaced the deprecated Angular animation trigger with a native CSS enter animation that respects reduced-motion preferences.
removed: Removed the @angular/animations peer dependency. The package is deprecated upstream and the library no longer needs it. Applications that installed it only for ng-hub-ui-toast can drop it; those using it elsewhere are unaffected.
Version 22.6.1 - 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.6.0 - 7/28/26, 12:00 AM
changed: Accent resolution now imports the canonical resolveHubAccent from ng-hub-ui-utils. The private copy under src/lib/shared/resolve-hub-accent.ts (used to resolve custom toast type accents) has been deleted in favour of the single, tested implementation shared family-wide. Behaviour is identical (the copy had not diverged): a bareword resolves to var(--hub-sys-color-<name>, <name>), a literal colour passes through unchanged, an empty value yields null.
added: NEW peer dependency: ng-hub-ui-utils >=22.7.0. Consumers must have ng-hub-ui-utils installed alongside this library (it is where resolveHubAccent lives). Users installing via ng add ng-hub-ui get it automatically; manual installs need npm i ng-hub-ui-utils.
Version 22.5.2 - 7/28/26, 12:00 AM
fixed: Toasts are announced to screen readers: role=alert/aria-live=assertive for error and warning, role=status/polite for the rest, with aria-atomic.
Version 22.5.1 - 7/9/26, 12:00 AM
fixed: NG0205: Injector has already been destroyed when a toast was raised just before teardown. The container is mounted from the .then() of a dynamic import(), which resolves on a later microtask; the mount now bails out when ApplicationRef.destroyed is set.
Version 22.5.0 - 7/7/26, 12:00 AM
added: Toast type accepts ANY colour. A custom type takes a registered accent name or a literal colour (#ff0000, rgb(...), oklch(...), a CSS named colour); the accent role family re-derives from it via color-mix. Built-in types are unchanged.
added: ng-hub-ui-toast/styles root entry. A styles/index.scss forwards hub-toast-theme, so @use 'ng-hub-ui-toast/styles' as *; exposes the mixin from the package root.
changed: BREAKING (packaging) — SCSS ships at ng-hub-ui-toast/styles. The theming mixin now builds to dist/toast/styles/... (was dist/toast/src/lib/styles/...), so @use 'ng-hub-ui-toast/styles' resolves. Update any @use that reached into src/lib/styles.
Version 22.4.0 - 7/2/26, 12:00 AM
changed: One derivation strategy — canonical accent slot. Every built-in data-type now re-bases only --hub-toast-accent; the role family (-subtle / -emphasis / -on) always recomputes locally from it, so a custom accent re-derives the whole family at runtime.
changed: The local derivations were unified to the canonical design-system formulas: -subtle mixes the accent at 12% over --hub-sys-surface-page (was 14%) and -emphasis at 80% over --hub-sys-color-ink (was 72% over --hub-sys-text-primary). Built-in types render as before; custom-type tints shift very slightly.
fixed: --hub-toast-shadow inline fallback aligned with the actual ds value of --hub-sys-shadow-md: 0 0.5rem 1rem rgba(0, 0, 0, 0.15). No change when the ds tokens are loaded.
changed: Added docs/css-variables-reference.md — the complete CSS custom-property reference — and realigned the README CSS-variable tables with the code.
Version 22.3.0 - 6/26/26, 12:00 AM
added: Open-set accent types. The built-in data-type map now also covers primary, secondary, neutral, light and dark alongside success / warning / info (and error→danger). Any other data-type keeps working with no recompile: define a single --hub-sys-color-<name>.
added: New derived accent roles --hub-toast-accent-subtle, --hub-toast-accent-emphasis and --hub-toast-accent-on, mixed locally from the single --hub-toast-accent slot.
added: The hub-toast-theme() mixin re-derives the accent role family whenever its $accent parameter is passed, so a brand accent on a custom selector recomputes -subtle / -emphasis / -on.
changed: BREAKING — canonical zindex token name: --hub-toast-container-z-index → --hub-toast-container-zindex (no hyphen, matching the --hub-sys-zindex-* convention).
changed: The accent role family and the progress-bar tint are now mixed in OKLCH instead of sRGB, for perceptually even tints across every accent. No token API change; tints shift very slightly.
Version 22.2.1 - 6/25/26, 12:00 AM
fixed: Design-token consistency pass: inline fallback defaults aligned with the canonical ng-hub-ui-ds values, and hardcoded literals (z-index, font-weight, line-height, radii, theme-aware colours) routed through their --hub-sys-* / --hub-ref-* tokens.
fixed: Toasts stack at the correct elevation: the container z-index resolves through --hub-sys-zindex-toast (1090) instead of a hardcoded 1050 (the modal-backdrop layer), so a toast can no longer be occluded by a modal backdrop.
Version 22.2.0 - 6/24/26, 12:00 AM
added: New hub-toast-theme() Sass mixin (styles/mixins/toast-theme) — re-skin a toast in one call: accent, surfaces, border/radius/shadow, spacing, typography, progress bar, close button and container tokens. Every parameter is optional, so only the ones passed are emitted.
added: ng-package.json ships the styles/ directory as a package asset, so the mixin is importable from consumers via @use 'ng-hub-ui-toast/styles/mixins/toast-theme'.
changed: Toast accent is now a full 1px border in the semantic colour instead of a thick left stripe. Built-in types take the full-strength --hub-sys-color-* accent rather than the muted border-subtle token, so their borders are noticeably more saturated. Purely visual.
removed: Removed the --hub-toast-accent-width token; the left accent stripe it sized no longer exists. The accent colour now drives the border and the progress bar through --hub-toast-accent.
Version 22.1.0 - 6/19/26, 12:00 AM
changed: Lowered the Angular peer dependency floor to `>=21.0.0`; the library now installs and runs in Angular 21 applications. No source or API changes.
Version 22.0.0 - 6/17/26, 12:00 AM
added: Initial release: `ToastService` with `success()`, `error()`, `warning()`, `info()`, `show()`, `remove()`, `clear()`.
added: `HubToastRef` with `onShown`, `onHidden`, `onTap` observables and `manualClose()` / `resetTimeout()`.
added: `ToastComponent` with signal-based auto-dismiss timer, progress bar and close button.
added: `ToastContainerComponent` with six position classes.
added: Accent system: `@each` loop for built-in types; `color-mix` open default for custom types.
added: `provideToast()` standalone provider function.
added: Angular animations: slide-in from edge on enter, fade-out on dismiss.
相关库
常见问题
在 Angular 里怎么弹出一条 toast 通知?
注入 HubToastService,调用 success()、error()、warning() 或 info(),传入消息和可选标题。模板里什么都不用放:服务会在首次调用时把自己的容器挂到 document.body 上。provideToast() 设定整个应用的默认值,第三个参数则只覆盖这一条通知。
怎么知道一条 toast 被关闭或被点击了?
每个展示方法都返回一个 HubToastRef,其中 onShown、onHidden 和 onTap 都是在 toast 关闭时完成的 observable,所以普通的 subscribe 会自行收尾,不需要 takeUntil。同一个 ref 还带有 manualClose() 和 resetTimeout(),以及可以传给 HubToastService.remove() 的 toastId。
能在 toast 里放按钮或 HTML 吗?
不能。标题和消息都以插值文本渲染,唯一的控件是关闭按钮,因此链接、标记和「撤销」按钮都不在范围内。点击整条 toast 可以通过 tapToDismiss 和 onTap observable 拿到;真正需要一个动作的场景应该用模态框或页面内的横幅。
toast 出现在哪里,能叠几条?
positionClass 接受六个取值之一——toast-top-right、toast-top-left、toast-top-center 以及对应的三个底部值——每个位置都有自己的容器,因此可以混用不同角落。newestOnTop 决定同一堆内的顺序,maxOpened 限制同时可见的数量(跨所有位置合计,而不是每个角落分别计算),preventDuplicates 会丢掉屏幕上已有消息的重复项。