Building a Global Toast Service in Angular with PrimeNG and Signals
Table of contents
Have you ever found yourself buried under a mountain of notifications in your Angular app, wondering if there's a slicker way to handle them? Well, you're in luck! Today, we're diving into the world of PrimeNG and Angular signals to build a super cool, global Toast service that'll make your app's notifications pop like never before. Whether you're using Angular 17 or the shiny new Angular 18, this guide has got you covered. Let's toast to cleaner code and happier users! 🥂
Getting Started: Displaying a Toast with a Button Click
Let's dive into writing a simple construction to display a toast notification when a button is clicked. We'll be using PrimeNG's p-toast
and p-button
components. Here's how you can get started:
Step 1: Set Up the HTML
First, we need to add the PrimeNG toast and a button to our app.component.html
file. The toast will handle displaying the notification, and the button will trigger the display.
<div class="card flex justify-content-center">
<p-toast/>
<p-button (click)="show()" label="Show"/>
</div>
Step 2: Handle the Button Click in TypeScript
Next, let's handle the button click to display a toast. In your app.component.ts
file, you'll need to add a method that uses the MessageService
provided by PrimeNG to show a toast notification.
First, make sure to import the MessageService
at the top of your app.component.ts
:
import { Component, effect, inject } from '@angular/core';
import { ToastModule } from 'primeng/toast';
import { Button } from 'primeng/button';
import { MessageService } from 'primeng/api';
@Component({
selector: 'app-root',
standalone: true,
imports: [ToastModule, Button],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
providers: [MessageService],
})
export class AppComponent {
readonly messageService = inject(MessageService);
show() {
this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Message Content' });
}
}
With these steps, you've set up a basic toast notification that appears when you click the "Show" button. This sets the stage for building a more sophisticated global Toast service.
Implementing a Global Toast Service
Instead of including the <p-toast>
component and MessageService
in every component where a toast might be displayed, it’s more efficient to centralize them in one place—specifically, the AppComponent
. This way, we can send messages that should be displayed as a toast via a service. Let's walk through how to achieve this by implementing a global Toast service. We'll use Angular signals instead of observables to react to incoming messages.
Step 1: Create the Toast Service
First, let's create a service that will handle toast messages and make them accessible to AppComponent
. We will use Angular signals for this purpose.
Create a new service toast.service.ts
:
import { computed, Injectable, signal } from '@angular/core';
import { Message } from 'primeng/api';
@Injectable({
providedIn: 'root',
})
export class ToastService {
private _message = signal<Message | undefined>(undefined);
message = computed(this._message);
postErrorMessage(summary: string, detail?: string) {
this.postMessage('error', summary, detail);
}
postWarningMessage(summary: string, detail?: string) {
this.postMessage('warn', summary, detail);
}
postSuccessMessage(summary: string, detail?: string) {
this.postMessage('success', summary, detail);
}
private postMessage(severity: string, summary: string, detail?: string) {
this._message.update(() => ({
severity: severity,
summary: summary,
detail: detail,
}));
}
}
Here’s a breakdown of the service:
We use
signal
to create a reactive state for_message
.The
computed
propertymessage
gives us a readonly access to the current value of_message
.Methods
postErrorMessage
,postWarningMessage
, andpostSuccessMessage
allow us to post messages with different severities.The
postMessage
method updates_message
with the new message details.
Step 2: Use the Service in AppComponent
Next, we’ll modify the AppComponent
to use this service and react to changes in the signal.
Update your app.component.ts
:
import { Component, effect, inject } from '@angular/core';
import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api';
@Component({
selector: 'app-root',
standalone: true,
imports: [ToastModule],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
providers: [MessageService],
})
export class AppComponent {
readonly messageService = inject(MessageService);
constructor() {
effect(() => {
const message = this.toastService.message();
if (message) {
this.messageService.add(message);
}
});
}
}
In this setup:
The
effect
function listens for changes in themessage
signal fromToastService
.When a new message is available, it calls
messageService.add()
to display the toast.
Using the Global Toast Service in Other Components
Now that we have our global Toast service set up, displaying a toast from any other component is straightforward. All you need to do is inject the ToastService
and use one of the severity functions to send a message.
import { Component } from '@angular/core';
import { ToastService } from '../toast.service'; // Adjust the import path as necessary
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit {
readonly toastService: ToastSevice = inject(ToastService)
ngOnInit(){
this.toastService.postSuccessMessage('Success', 'This is a success message from MyComponent');
}
}
Summary
By centralizing the toast display logic in the ToastService
, you can easily trigger toast notifications from any component in your Angular application. Simply inject the ToastService
and use the appropriate method to display your toast message. This approach keeps your code clean, maintainable, and efficient.