Building a Global Toast Service in Angular with PrimeNG and Signals

Building a Global Toast Service in Angular with PrimeNG and Signals

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 property message gives us a readonly access to the current value of _message.

  • Methods postErrorMessage, postWarningMessage, and postSuccessMessage 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 the message signal from ToastService.

  • 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.