How to customize an Alert Message Box in Angular?
文章目錄
本篇文章,將以 Angular 17 實作一個簡單的 Alert 通知系統。 包含 Alert 模型、Alert 服務和 Alert 元件的建立,並且使用 Angular Animations 添加動畫效果。
建立模型
建立 Alert
模型,包含 type
和 message
兩個屬性,並建立 enum AlertType
來表示不同類型的通知。
export interface IAlertOption {
message: string;
type?: AlertType;
autoRemove?: boolean;
removeAfter?: number;
}
export class Alert {
type: AlertType;
message: string;
constructor(msg: string, type: AlertType = AlertType.Success) {
this.type = type;
this.message = msg;
}
}
export enum AlertType {
Error,
Success,
Info,
}
建立服務
使用 Angular CLI 指令,建立 Alert 服務,管理通知邏輯。
ng g s alert
建立
BehaviorSubject
發佈保留通知。value
:取得保留的所有通知。next
:用來發佈通知給訂閱者。
通知邏輯包含,發佈通知(set)、取代最後通知的訊息(replace)、移除通知(remove)、自動移除通知(autoRemove)。
export class AlertService { alerts$ = new BehaviorSubject<Alert[]>([]); constructor() { } set({message, type, autoRemove = true, removeAfter}: IAlertOption) { const alert = new Alert(message, type); this.alerts$.next([...this.alerts$.value, alert]); if(autoRemove) this.autoRemove(alert, removeAfter).subscribe(); } replace(msg: string) { const alerts = this.alerts$.value; const length = alerts.length; if(length > 0) alerts[length - 1].message = msg; } remove(alert: Alert) { this.alerts$.next(this.alerts$.value.filter(_ => _ != alert)); } autoRemove(alert: Alert, removeAfter: number = 10000) { // 10 sec return timer(removeAfter).pipe( mergeMap(() => of(this.remove(alert))) ); } }
建立動畫效果
建立可重複使用的動畫。
import { animate, style, transition, trigger } from "@angular/animations"; export const fadeInOut = trigger('fadeInOut', [ transition(':enter', [ style({opacity: 0}), animate(500, style({opacity: 1})) ]), transition(':leave', [ animate(500, style({opacity: 0})) ]) ]);
啟用動畫模組。
// app.config.ts import { provideAnimations } from '@angular/platform-browser/animations'; export const appConfig: ApplicationConfig = { providers: [ provideAnimations() ] };
建立元件
使用 Angular CLI 指令建立 Alert 元件。
ng g c alert
實現元件
animations: [fadeInOut]
:引入動畫效果。alerts$
:從 AlertService 取得發佈通知的BehaviorSubject
,並在模板中使用AsyncPipe
訂閱。close
:手動關閉通知。color
:根據AlertType
設置顏色樣式。
@Component({ selector: 'app-alert', standalone: true, imports: [AsyncPipe, NgClass], animations: [fadeInOut], templateUrl: './alert.component.html', styleUrl: './alert.component.css' }) export class AlertComponent { alerts$: Observable<Alert[]> = this._service.alerts$; constructor(private _service: AlertService) {} close(alert: Alert) { this._service.remove(alert); } color(type: AlertType): string { const colorMap: { [key in AlertType]: string } = { [AlertType.Error]: 'alert-danger', [AlertType.Success]: 'alert-success', [AlertType.Info]: 'alert-info' }; return colorMap[type] || 'alert-info'; } }
建構模板
alerts$ | async
:使用AsyncPipe
訂閱通知。[@fadeInOut]
:綁定動畫觸發器。
<!-- alert.component.html --> @if(alerts$ | async; as alerts) { <div class="alert"> <ul class="list"> @for (alert of alerts; track $index) { <li class="list-item" [ngClass]="color(alert.type)" [@fadeInOut]> <button class="btn btn-close" (click)="close(alert)">x</button> <span>{{ alert.message }}</span> </li> } </ul> </div> }
裝飾元件
/* alert.component.css */ .alert { position: fixed; right: 0; z-index: 2000; width: 100%; max-width: 400px; } .list { list-style: none; padding: 0; margin: 0; } .list-item { position: relative; padding: 1.5em; margin: 0 1em; border-radius: .25em; box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; } .list-item:not(:last-child) { margin-bottom: 1em; } .btn-close { position: absolute; top: 8px; right: 12px; color: inherit; padding: 0; } .btn-close:hover { filter: brightness(1.5); } .alert-danger { color: #721c24; background-color: #f8d7da; } .alert-success { color: #155724; background-color: #d4edda; } .alert-info { color: #0c5460; background-color: #d1ecf1; }