@libs-ui/services-notification
v0.2.357-4
Published
> Service toast notification cho Angular — hiển thị 4 kiểu thông báo (success, error, info, warn), quản lý queue tự động, tích hợp ngx-translate, và hỗ trợ bridge sang micro-frontend qua iFrame messaging.
Downloads
4,629
Readme
@libs-ui/services-notification
Service toast notification cho Angular — hiển thị 4 kiểu thông báo (success, error, info, warn), quản lý queue tự động, tích hợp ngx-translate, và hỗ trợ bridge sang micro-frontend qua iFrame messaging.
Giới thiệu
LibsUiNotificationService là service singleton (providedIn: 'root') dùng để hiển thị toast notification động trực tiếp vào document.body thông qua LibsUiDynamicComponentService. Service tự quản lý queue tối đa 5 thông báo đồng thời theo cơ chế FIFO, tự động stack vị trí các toast, và có khả năng phát hiện môi trường iFrame để bridge thông báo lên parent shell trong kiến trúc micro-frontend.
Tính năng
- ✅ 4 kiểu toast:
success(xanh lá),error(đỏ),info(xanh dương),warn(vàng) - ✅ Queue tối đa 5 — FIFO auto-remove khi overflow
- ✅ Tích hợp ngx-translate — truyền translation key hoặc text thường đều được
- ✅
interpolateParamsvới XSS-safe escaping tự động quaescapeHtml() - ✅ Callback
onClick/onMouseentertrên từng toast - ✅ Tùy chỉnh
timeRemove(ms) vàpositionRight(px) - ✅ Dynamic stacking — tự tính lại vị trí top/right sau mỗi add/remove
- ✅ iFrame bridge cho micro-frontend — tự phát hiện
isEmbedFrame()và gửi PostMessage lên parent - ✅ Browser native Notification API (
systemSpawnNotification) trả vềObservable
Khi nào sử dụng
- Thông báo kết quả sau action: lưu, xóa, cập nhật — dùng
successhoặcerror - Thông báo lỗi hệ thống hoặc lỗi network API — dùng
errorhoặcwarn - Thông báo thông tin cho user (tips, hướng dẫn ngắn) — dùng
info - Cần hiển thị notification từ micro-frontend (child iFrame) lên parent shell — dùng
init()+ iFrame bridge - Cần push native OS/browser notification khi tab không active — dùng
systemSpawnNotification()
Cài đặt
npm install @libs-ui/services-notificationImport
import { LibsUiNotificationService } from '@libs-ui/services-notification';Service được đăng ký providedIn: 'root' — không cần thêm vào providers[] của module hay component.
Ví dụ sử dụng
1. Cơ bản — 4 kiểu toast
import { Component, inject } from '@angular/core';
import { LibsUiNotificationService } from '@libs-ui/services-notification';
@Component({
standalone: true,
selector: 'app-example',
template: `
<button (click)="handlerSave()">Lưu</button>
<button (click)="handlerDelete()">Xóa</button>
`,
})
export class ExampleComponent {
private readonly notif = inject(LibsUiNotificationService);
handlerSave() {
this.notif.showCompTypeTextSuccess('Lưu thành công!');
}
handlerDelete() {
this.notif.showCompTypeTextError('Xóa thất bại. Vui lòng thử lại.');
}
handlerInfo() {
this.notif.showCompTypeTextInfo('Phiên làm việc sẽ hết hạn sau 5 phút.');
}
handlerWarning() {
this.notif.showCompTypeTextWarning('Dung lượng lưu trữ gần đầy.');
}
}2. Đầy đủ config — title, timeRemove, positionRight, callback
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
import { LibsUiNotificationService } from '@libs-ui/services-notification';
@Component({
standalone: true,
selector: 'app-create-user',
template: `<button (click)="handlerCreate()">Tạo tài khoản</button>`,
})
export class CreateUserComponent {
private readonly notif = inject(LibsUiNotificationService);
private readonly router = inject(Router);
handlerCreate() {
this.notif.showCompTypeTextSuccess('Tạo tài khoản thành công!', {
title: 'Thành công', // tiêu đề (optional)
timeRemove: 5000, // ms — tự động đóng sau 5 giây (default: 3000)
positionRight: 24, // px từ phải màn hình (default: 12)
eventName: 'click', // 'click' | 'mouseenter' — trigger callback
callback: () => this.router.navigate(['/users']),
interpolateParams: { // params cho ngx-translate interpolation (tự động escaped)
name: 'Nguyễn Văn A',
},
});
}
}3. Dùng với ngx-translate key và interpolation
import { Component, inject } from '@angular/core';
import { LibsUiNotificationService } from '@libs-ui/services-notification';
@Component({
standalone: true,
selector: 'app-report',
template: `<button (click)="handlerDelete()">Xóa báo cáo</button>`,
})
export class ReportComponent {
private readonly notif = inject(LibsUiNotificationService);
handlerDelete() {
// Truyền translation key — ngx-translate xử lý tự động
this.notif.showCompTypeTextSuccess('notifications.save_success');
// Với interpolation params (tự động escape HTML để ngăn XSS):
this.notif.showCompTypeTextError('notifications.delete_error', {
interpolateParams: { itemName: 'Báo cáo tháng 1' },
});
// JSON: { "notifications": { "delete_error": "Không thể xóa '{{itemName}}'. Vui lòng thử lại." } }
// Kết quả: "Không thể xóa 'Báo cáo tháng 1'. Vui lòng thử lại."
}
}4. Callback khi user tương tác với toast
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
import { LibsUiNotificationService } from '@libs-ui/services-notification';
@Component({
standalone: true,
selector: 'app-inbox',
template: `<button (click)="handlerNewMessage()">Kiểm tra tin nhắn</button>`,
})
export class InboxComponent {
private readonly notif = inject(LibsUiNotificationService);
private readonly router = inject(Router);
handlerNewMessage() {
// Callback khi user click vào toast
this.notif.showCompTypeTextInfo('Bạn có thông báo mới. Click để xem.', {
eventName: 'click',
timeRemove: 8000,
callback: () => {
this.router.navigate(['/notifications']);
},
});
}
handlerStorageWarning() {
// Callback khi user hover vào toast
this.notif.showCompTypeTextWarning('Dung lượng gần đầy. Hover để xem chi tiết.', {
eventName: 'mouseenter',
timeRemove: 6000,
callback: () => this.showStorageDetail(),
});
}
private showStorageDetail() {
// xử lý hiển thị chi tiết
}
}5. iFrame Bridge — Micro-frontend
// === AppComponent của Shell (parent) ===
import { Component, inject } from '@angular/core';
import { LibsUiNotificationService } from '@libs-ui/services-notification';
@Component({
standalone: true,
selector: 'app-root',
template: `<router-outlet />`,
})
export class AppComponent {
private readonly notif = inject(LibsUiNotificationService);
constructor() {
// Gọi 1 lần duy nhất — service có guard isInit để tránh duplicate listener
this.notif.init(
['MICRO_SITE_PUSH_MESSAGE_FROM_CHILD'], // message names để lắng nghe từ child
'MICRO_SITE_PUSH_MESSAGE_FROM_CHILD', // tên PostMessage gửi lên parent (optional)
);
}
}
// === Micro-frontend child (embedded trong iFrame) ===
// Gọi notification bình thường — service tự phát hiện isEmbedFrame()
// và gửi PostMessage lên parent thay vì render tại chỗ
@Component({
standalone: true,
selector: 'app-upload',
template: `<button (click)="handlerUpload()">Upload</button>`,
})
export class UploadComponent {
private readonly notif = inject(LibsUiNotificationService);
handlerUpload() {
this.notif.showCompTypeTextSuccess('Upload hoàn tất!');
// → parent shell nhận PostMessage và render notification thay vì child
}
}6. Browser Native Notification
import { Component, inject } from '@angular/core';
import { LibsUiNotificationService } from '@libs-ui/services-notification';
@Component({
standalone: true,
selector: 'app-push-setup',
template: `
<button (click)="handlerRequestPermission()">Bật thông báo</button>
<button (click)="handlerSpawnNotification()">Gửi thông báo</button>
`,
})
export class PushSetupComponent {
private readonly notif = inject(LibsUiNotificationService);
handlerRequestPermission() {
// Xin quyền browser native notification
this.notif.systemRequestPermission();
}
handlerSpawnNotification() {
// Hiển thị native notification (chỉ khi đã granted)
this.notif
.systemSpawnNotification('Thông báo mới', {
body: 'Bạn có 3 tin nhắn chưa đọc từ Team',
icon: '/assets/icons/icon-192x192.png',
tag: 'inbox-notification',
})
.subscribe({
next: (event) => {
// Trả về { notification, event } khi show hoặc click
},
error: (err) => {
// Notification lỗi
},
complete: () => {
// Notification đã đóng
},
});
}
}Methods (Service)
| Method | Signature | Mô tả |
|---|---|---|
| showCompTypeTextSuccess | (message: string, config?: INotificationTextPublicConfig): void | Hiển thị toast màu xanh lá — thành công |
| showCompTypeTextError | (message: string, config?: INotificationTextPublicConfig): void | Hiển thị toast màu đỏ — lỗi |
| showCompTypeTextInfo | (message: string, config?: INotificationTextPublicConfig): void | Hiển thị toast màu xanh dương — thông tin |
| showCompTypeTextWarning | (message: string, config?: INotificationTextPublicConfig): void | Hiển thị toast màu vàng — cảnh báo |
| init | (messageNameHandler: string[], messageNamePostToParent?: string): void | Khởi tạo iFrame bridge listener — gọi 1 lần trong AppComponent của shell |
| systemRequestPermission | (): void | Xin quyền browser native notification |
| systemSpawnNotification | (title: string, options?: NotificationOptions): Observable<unknown> | Hiển thị browser native notification, trả về Observable theo vòng đời notification |
| get TranslateService | TranslateService | Getter trả về instance của TranslateService đang inject |
Types & Interfaces
import {
INotificationTextPublicConfig,
INotificationTextConfig,
INotificationQueue,
INotificationPushFromIFrame,
} from '@libs-ui/services-notification';INotificationTextPublicConfig
Config truyền vào các method showCompTypeText*():
interface INotificationTextPublicConfig {
title?: string; // Tiêu đề hiển thị trên toast
timeRemove?: number; // ms — tự động đóng (default: 3000)
positionRight?: number; // px từ phải màn hình (default: 12)
eventName?: 'click' | 'mouseenter'; // Event trigger callback
callback?: () => void; // Hàm gọi khi user tương tác
interpolateParams?: Record<string, any>; // Params ngx-translate (tự động escaped)
}| Field | Type | Default | Mô tả | Ví dụ |
|---|---|---|---|---|
| title | string | undefined | Tiêu đề hiển thị phía trên message trong toast | title: 'Thành công' |
| timeRemove | number (ms) | 3000 | Thời gian tự động đóng tính bằng milliseconds | timeRemove: 5000 |
| positionRight | number (px) | 12 | Khoảng cách từ phải màn hình tính bằng pixel | positionRight: 24 |
| eventName | 'click' \| 'mouseenter' | undefined | Event DOM sẽ trigger callback | eventName: 'click' |
| callback | () => void | undefined | Hàm gọi khi user thực hiện event tương ứng | callback: () => router.navigate(['/']) |
| interpolateParams | Record<string, any> | undefined | Params truyền vào ngx-translate interpolation, tự động escape HTML | interpolateParams: { name: 'An' } |
INotificationTextConfig (Internal)
Config nội bộ dùng khi tạo component động — thường không dùng trực tiếp:
interface INotificationTextConfig {
type: 'success' | 'info' | 'error' | 'warn'; // Kiểu toast
timeRemove: number; // ms
positionRight?: number; // px
zIndex?: number; // CSS z-index (default: 2000)
eventName?: 'click' | 'mouseenter';
callback?: () => void;
}INotificationPushFromIFrame (Internal)
Payload nhận từ child iFrame qua PostMessage:
interface INotificationPushFromIFrame {
functionName: 'success' | 'info' | 'error' | 'warn';
message: string;
title?: string;
timeRemove?: number;
}Lưu ý quan trọng
⚠️ Queue MAX 5 — FIFO: Nếu đang có đủ 5 toast trên màn hình, toast cũ nhất sẽ tự bị remove (kèm hủy timeout của nó) khi toast mới xuất hiện. Hành vi này không thể tắt.
⚠️ interpolateParams XSS-safe: Mỗi value trong interpolateParams tự động qua escapeHtml() trước khi truyền vào ngx-translate. Đây là bảo vệ XSS cho user-generated content. Message string gốc không bị escape — phải là translation key hoặc text an toàn.
⚠️ init() chỉ gọi 1 lần: Service có guard isInit signal — gọi init() nhiều lần an toàn, chỉ listener đầu tiên được đăng ký. Nên gọi trong AppComponent constructor của shell.
⚠️ iFrame auto-detection: Service tự gọi isEmbedFrame() — không cần config thêm trong child app. Khi chạy trong iFrame, payload được encrypt() trước khi gửi lên parent qua PostMessage.
⚠️ systemSpawnNotification yêu cầu granted: Observable sẽ complete() ngay lập tức nếu browser không hỗ trợ Notification API hoặc quyền chưa được cấp ('granted'). Luôn gọi systemRequestPermission() trước.
⚠️ Dynamic stacking dùng getBoundingClientRect(): Vị trí top/right của các toast được tính lại sau mỗi add/remove thông qua setTimeout để đảm bảo DOM đã render. Tránh can thiệp vào style inline top/right của các element toast từ bên ngoài.
Demo
npx nx serve core-uiTruy cập: http://localhost:4500/services/notification
