Cloud Messaging

Cloud Messaging

Cross-platform push notifications and real-time message delivery via Firebase Cloud Messaging (FCM).

Introduction

Firebase Cloud Messaging (FCM) provides a reliable and battery-efficient connection between your server and devices that allows you to deliver and receive messages and notifications on iOS, Android, and the web at no cost.

In AngularFire v21, the messaging module provides clean APIs for requesting notification permissions, retrieving registration tokens, and listening to foreground push messages.

Key Capabilities:

  • Browser notification permission request management
  • FCM registration token retrieval with VAPID key support
  • Reactive foreground message stream via onMessage()
  • Service Worker background notification handling

Setup & Configuration

Add provideMessaging() to your ApplicationConfig:

import { ApplicationConfig } from '@angular/core';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getMessaging, provideMessaging } from '@angular/fire/messaging';

export const appConfig: ApplicationConfig = {
  providers: [
    provideFirebaseApp(() => initializeApp({ /* firebaseConfig */ })),
    provideMessaging(() => getMessaging()),
  ],
};

Service Worker Configuration

Create public/firebase-messaging-sw.js to handle background push notifications when your Angular app tab is closed:

// public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/11.0.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/11.0.0/firebase-messaging-compat.js');

firebase.initializeApp({
  apiKey: "YOUR_API_KEY",
  projectId: "YOUR_PROJECT_ID",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID",
});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: '/favicon.ico',
  };

  self.registration.showNotification(notificationTitle, notificationOptions);
});

FCM Notification Service with Signals

Create an Angular service to manage permissions, tokens, and incoming messages:

import { Injectable, inject, signal } from '@angular/core';
import { Messaging, getToken, onMessage, MessagePayload } from '@angular/fire/messaging';

@Injectable({ providedIn: 'root' })
export class NotificationService {
  private messaging = inject(Messaging);

  readonly fcmToken = signal<string | null>(null);
  readonly currentMessage = signal<MessagePayload | null>(null);
  readonly permissionStatus = signal<NotificationPermission>('default');

  constructor() {
    if (typeof window !== 'undefined' && 'Notification' in window) {
      this.permissionStatus.set(Notification.permission);
      this.listenToMessages();
    }
  }

  // Request browser permission and obtain device token
  async requestPermission(vapidKey: string): Promise<string | null> {
    try {
      const permission = await Notification.requestPermission();
      this.permissionStatus.set(permission);

      if (permission === 'granted') {
        const token = await getToken(this.messaging, { vapidKey });
        this.fcmToken.set(token);
        return token;
      }
      return null;
    } catch (err) {
      console.error('An error occurred while retrieving token. ', err);
      return null;
    }
  }

  // Listen to foreground notifications
  private listenToMessages(): void {
    onMessage(this.messaging, (payload) => {
      console.log('Message received in foreground: ', payload);
      this.currentMessage.set(payload);
    });
  }
}

Displaying Notifications in Components

import { Component, inject } from '@angular/core';
import { NotificationService } from './notification.service';

@Component({
  selector: 'app-notification-banner',
  template: `
    <!-- Permission Request Prompt -->
    @if (notifications.permissionStatus() === 'default') {
      <div class="permission-banner">
        <p>Enable push notifications to receive real-time updates.</p>
        <button (click)="enableNotifications()">Enable</button>
      </div>
    }

    <!-- In-App Notification Toast -->
    @if (notifications.currentMessage(); as msg) {
      <div class="toast">
        <h4>{{ msg.notification?.title }}</h4>
        <p>{{ msg.notification?.body }}</p>
      </div>
    }
  `,
})
export class NotificationBannerComponent {
  readonly notifications = inject(NotificationService);

  enableNotifications() {
    this.notifications.requestPermission('YOUR_PUBLIC_VAPID_KEY');
  }
}