Complete Angular integration with services and dependency injection
Try the Angular integration in action:
npm install modern-toasts
import toast from 'modern-toasts';
@Component({
selector: 'app-example',
template: `
<button (click)="showToast()">Show Toast</button>
`
})
export class ExampleComponent {
showToast() {
toast.success('Hello from Angular!');
}
}
// services/toast.service.ts
import { Injectable } from '@angular/core';
import toast from 'modern-toasts';
@Injectable({
providedIn: 'root'
})
export class ToastService {
constructor() {
// Configure toast on service initialization
toast.configure({
position: 'bottom-right',
maxVisibleStackToasts: 3,
enableBorderAnimation: true,
enableFillAnimation: true,
pauseBackgroundToastsOnHover: true
});
}
success(message: string, options?: any): string {
return toast.success(message, options);
}
error(message: string, options?: any): string {
return toast.error(message, options);
}
info(message: string, options?: any): string {
return toast.info(message, options);
}
warning(message: string, options?: any): string {
return toast.warning(message, options);
}
dismiss(id: string): void {
toast.dismiss(id);
}
dismissAll(): void {
toast.dismissAll();
}
configure(config: any): void {
toast.configure(config);
}
// Event listeners
on(event: string, callback: Function): void {
toast.on(event, callback);
}
off(event: string, callback: Function): void {
toast.off(event, callback);
}
}
import { Component } from '@angular/core';
import { ToastService } from './services/toast.service';
@Component({
selector: 'app-user-profile',
template: `
<button (click)="saveProfile()">Save Profile</button>
`
})
export class UserProfileComponent {
constructor(private toastService: ToastService) {}
async saveProfile() {
try {
await this.userService.saveProfile();
this.toastService.success('Profile saved successfully!');
} catch (error) {
this.toastService.error('Failed to save profile');
}
}
}
// interceptors/toast-error.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { ToastService } from '../services/toast.service';
@Injectable()
export class ToastErrorInterceptor implements HttpInterceptor {
constructor(private toastService: ToastService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
let errorMessage = 'An error occurred';
switch (error.status) {
case 400:
errorMessage = 'Bad request. Please check your input.';
break;
case 401:
errorMessage = 'Unauthorized. Please log in again.';
break;
case 403:
errorMessage = 'Access forbidden.';
break;
case 404:
errorMessage = 'Resource not found.';
break;
case 500:
errorMessage = 'Server error. Please try again later.';
break;
default:
errorMessage = error.error?.message || 'Network error occurred.';
}
this.toastService.error(errorMessage, {
autoDismiss: 5000
});
return throwError(error);
})
);
}
}
// app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ToastErrorInterceptor } from './interceptors/toast-error.interceptor';
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ToastErrorInterceptor,
multi: true
}
]
})
export class AppModule {}
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ToastService } from './services/toast.service';
@Component({
selector: 'app-contact-form',
template: `
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
<input formControlName="email" placeholder="Email">
<textarea formControlName="message" placeholder="Message"></textarea>
<button type="submit" [disabled]="contactForm.invalid">
Submit
</button>
</form>
`
})
export class ContactFormComponent {
contactForm: FormGroup;
constructor(
private fb: FormBuilder,
private toastService: ToastService
) {
this.contactForm = this.fb.group({
email: ['', [Validators.required, Validators.email]],
message: ['', [Validators.required, Validators.minLength(10)]]
});
}
onSubmit() {
if (this.contactForm.invalid) {
this.showValidationErrors();
return;
}
this.submitForm();
}
private showValidationErrors() {
const controls = this.contactForm.controls;
if (controls['email'].errors?.['required']) {
this.toastService.error('Email is required');
} else if (controls['email'].errors?.['email']) {
this.toastService.error('Please enter a valid email');
}
if (controls['message'].errors?.['required']) {
this.toastService.error('Message is required');
} else if (controls['message'].errors?.['minlength']) {
this.toastService.error('Message must be at least 10 characters');
}
}
private async submitForm() {
try {
await this.contactService.submit(this.contactForm.value);
this.toastService.success('Message sent successfully!', {
autoDismiss: 5000
});
this.contactForm.reset();
} catch (error) {
this.toastService.error('Failed to send message');
}
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { ToastService } from './toast.service';
@Injectable({
providedIn: 'root'
})
export class DataService {
private loadingSubject = new BehaviorSubject<boolean>(false);
public loading$ = this.loadingSubject.asObservable();
constructor(
private http: HttpClient,
private toastService: ToastService
) {}
async fetchData(): Promise<any> {
this.loadingSubject.next(true);
const loadingToastId = this.toastService.info('Loading data...', {
autoDismiss: 0
});
try {
const data = await this.http.get('/api/data').toPromise();
this.toastService.dismiss(loadingToastId);
this.toastService.success('Data loaded successfully!');
return data;
} catch (error) {
this.toastService.dismiss(loadingToastId);
throw error; // Let interceptor handle the error toast
} finally {
this.loadingSubject.next(false);
}
}
}
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';
import { ToastService } from './toast.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router,
private toastService: ToastService
) {}
canActivate(): boolean {
if (this.authService.isAuthenticated()) {
return true;
}
this.toastService.warning('Please log in to access this page', {
autoDismiss: 4000
});
this.router.navigate(['/login']);
return false;
}
}
// components/toast-trigger.component.ts
import { Component, Input } from '@angular/core';
import { ToastService } from '../services/toast.service';
@Component({
selector: 'app-toast-trigger',
template: `
<button
[class]="buttonClass"
(click)="triggerToast()"
[disabled]="disabled"
>
{{ label }}
</button>
`
})
export class ToastTriggerComponent {
@Input() type: 'success' | 'error' | 'info' | 'warning' = 'info';
@Input() message: string = '';
@Input() label: string = 'Show Toast';
@Input() disabled: boolean = false;
@Input() options: any = {};
constructor(private toastService: ToastService) {}
get buttonClass(): string {
return `btn btn-${this.type}`;
}
triggerToast(): void {
const toastMessage = this.message || `${this.type} toast triggered!`;
this.toastService[this.type](toastMessage, this.options);
}
}
<app-toast-trigger
type="success"
message="Operation completed successfully!"
label="Complete Task"
[options]="{ autoDismiss: 3000 }">
</app-toast-trigger>
<app-toast-trigger
type="error"
message="Something went wrong!"
label="Trigger Error">
</app-toast-trigger>
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { ToastService } from './services/toast.service';
@Component({
selector: 'app-root',
template: `<router-outlet></router-outlet>`
})
export class AppComponent implements OnInit {
constructor(private toastService: ToastService) {}
ngOnInit() {
// Configure toast globally
this.toastService.configure({
position: 'top-right',
maxVisibleStackToasts: 4,
defaultDuration: 4000,
enableBorderAnimation: true,
enableFillAnimation: true,
pauseBackgroundToastsOnHover: true,
customCSS: `
.toast-container {
z-index: 9999;
}
.toast-angular {
border-left: 4px solid #dd0031;
}
`
});
// Listen to toast events
this.toastService.on('show', (toastData) => {
console.log('Toast shown:', toastData);
});
}
}
// interfaces/toast.interface.ts
export interface CustomToastOptions {
autoDismiss?: number;
backgroundColor?: string;
textColor?: string;
borderColor?: string;
showCloseButton?: boolean;
pauseOnHover?: boolean;
className?: string;
icon?: string;
animationDirection?: 'left-to-right' | 'right-to-left' | 'top-to-bottom' | 'bottom-to-top';
userId?: string;
component?: string;
}
export interface ToastResponse {
id: string;
success: boolean;
}
// Enhanced service with types
import { CustomToastOptions, ToastResponse } from '../interfaces/toast.interface';
@Injectable({
providedIn: 'root'
})
export class TypedToastService {
success(message: string, options?: CustomToastOptions): string {
return toast.success(message, options);
}
error(message: string, options?: CustomToastOptions): string {
return toast.error(message, options);
}
// Async toast with promise
async showWithConfirmation(
message: string,
type: 'success' | 'error' | 'info' | 'warning' = 'info'
): Promise<ToastResponse> {
const id = toast[type](message, { autoDismiss: 0 });
return new Promise((resolve) => {
const handleDismiss = () => {
toast.off('dismiss', handleDismiss);
resolve({ id, success: true });
};
toast.on('dismiss', handleDismiss);
});
}
}