Implementación de un sistema de notificaciones en Angular

3
13684

Índice de contenidos

1. Entorno

Este tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil Mac Book Pro 15″ (2,3 Ghz Intel Core i7, 16 GB DDR3)
  • Sistema Operativo: macOS Sierra
  • @angular/cli 1.06

2. Introducción

A poco que hayas desarrollado alguna aplicación de front te habrás encontrado con el caso de uso de tener que mostrar una notificación al usuario ya sea por un error o para informar de que todo ha ido bien.

Si la aplicación la has desarrollado en Angular te habrás visto en la necesidad de tener que poner en cada componente la lógica de notificaciones al usuario, o habrás dejado el típico console.log(error) 😉

Pero, amigo, la programación reactiva ha llegado para solucionar muchos de estos casos de usos comunes de una forma elegante, efectiva y sobre todo, que te evita duplicar código en componentes.

3. Vamos al lío

Lo primero que vamos a hacer es definir nuestro modelo creando una interfaz llamada «Notificacion» con el siguiente contenido:

export interface Notificacion {
    severity: string;
    summary: string;
    detail: string;
};

Como ves simplemente definimos que nuestra notificación va a definir una severidad, un texto de resumen y un detalle todos ellos de tipo string.

Ahora creamos un servicio de bus de notificaciones donde vamos a tener una variable de tipo Subject que nos va a permitir enviar y recibir las notificaciones a través de este bus. Además nos vamos a definir una serie de métodos específicos para cada una de las severidades de la notificación.

El contenido del servicio podría ser parecido a este:

import { Notificacion } from './notificacion';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class NotificacionesBusService {

  showNotificacionSource: Subject = new Subject();

  getNotificacion(): Observable {
    return this.showNotificacionSource.asObservable();
  }

  showError(msg: string, summary?: string) {
    this.show('error', summary, msg);
  }

  showSuccess(msg: string, summary?: string) {
    this.show('success', summary, msg);
  }

  showInfo(msg: string, summary?: string) {
    this.show('info', summary, msg);
  }

  showWarn(msg: string, summary?: string) {
    this.show('warn', summary, msg);
  }

  private show(severity: string, summary: string, msg: string) {
    const notificacion: Notificacion = {
      severity: severity,
      summary: summary,
      detail: msg
    };

    this.notify(notificacion);

  }

  private notify(notificacion: Notificacion): void {
    this.showNotificacionSource.next(notificacion);
  }

}

Los puntos clave de esta implementación son por un lado el envío de la notificación al bus con el método «notify» y por otro el método «getNotificacion» que permite devolver el observable al que se van a subscribir los escuchantes del bus.

A fin de probar el comportamiento podemos implementar un test con el siguiente contenido:

import { TestBed } from '@angular/core/testing';
import { async } from '@angular/core/testing';
import { NotificacionesBusService } from './notificaciones-bus.service';
import { Notificacion } from './notificacion';

describe('Notificaciones bus service', () => {

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            providers: [NotificacionesBusService]
        });
    }));

    it('should enviar y obtener una notificacion', () => {
        const msg = 'Prueba';
        const service: NotificacionesBusService = TestBed.get(NotificacionesBusService);
        service.getNotificacion().subscribe(
            noti => {
                console.log(noti.detail);
                expect(noti.detail).toEqual(msg);
            },
            error => {
                fail(error);
            }
        );
        service.showError(msg);
        service.showSuccess(msg);
        service.showWarn(msg);
        service.showInfo(msg);
    });
});

En el resultado del test tienes que ver que se muestra cuatro veces el contenido del detalle. Fíjate que estamos usando Subject en la implementación del servicio, porque estamos seguros de que primero se va a subscribir antes de empezar a enviar nada.

Nota: Te aconsejo que modifiques el test y pongas el subscribe después de las llamadas a los métodos. Ahora verás que no muestras los mensajes por consola. Esto es debido a que con el Subject solo recibes lo que envías después de la subscripción. En caso de no estar seguros de que la subscripción se realiza antes del envío del primer mensaje, cambia Subject por ReplaySubject en la implementación del servicio.

De esta forma podemos incluir la lógica de recibir la notificación en el componente principal de la aplicación a fin de que esté disponible desde cualquier punto de la aplicación; y hacer uso de una librería de componentes como PrimeNG con un componente Growl para pintar de forma «bonita» la notificación por pantalla.

Este sería el contenido del fichero app.component.html:

<p-growl [(value)]="msgs" id="errorMessages"></p-growl>
<router-outlet></router-outlet>

Dentro del fichero app.component.ts tenemos que inyectar a través del constructor el servicio «NotificacionesBusService» antes creado y establecer en el método ngOnInit() la lógica de subscripción al bus para atender a las notificaciones que se envíen:

constructor(private notificacionesBus: NotificacionesBusService) { }

ngOnInit() {
     this.notificacionesSub = 
     this.notificacionesBus.getNotificacion().subscribe(
        (notificacion: Notificacion) => {
            this.msgs = [];
            this.msgs.push(notificacion);
         }
      );
}

No olvidéis hacer la desubscripción en el método ngOnDestroy() de todas las subscripciones que hagáis, evitando «memory leaks»

 ngOnDestroy() {
        if (this.notificacionesSub) {
            this.notificacionesSub.unsubscribe();
        }
    }

Ahora desde cualquier parte de la aplicación, ya sea otro componente o servicio, podemos inyectar el servicio NotificacionesBusService y llamar a cualquiera de sus métodos con la información de la notificación que queramos mostrar sin preocuparnos por nada más, ya que este envío será atendido por la lógica del componente principal y el mensaje se mostrará por pantalla gracias al componente de PrimeNG, el cual previamente tendréis que instalar como dependencia en vuestro proyecto Angular. El componente que utilicéis es independiente de la lógica de envío de notificaciones.

Este podría ser un ejemplo de uso desde otro componente:


 constructor( private notificacionBus: NotificacionesBusService) { }

this.service.hagoAlgo(info)
        .then(res => {
          this.notificacionBus.showSuccess('Proceso con éxito');
        })
        .catch(err => {
          this.notificacionBus.showError('Ha ocurrido un error');
        });

4. Conclusiones

Haz el cálculo de la cantidad de código duplicado que te ahorras con esta técnica y lo que ganas en mantenibilidad y legibilidad y como yo gritarás bien alto: !Viva la programación reactiva con RxJS¡

Cualquier duda o sugerencia en la zona de comentarios.

Saludos.

3 COMENTARIOS

  1. Hola buen día,

    Le eh echado un vistazo a tu código, solo tengo una duda, a mi me piden hacer un microservicio de notificaciones, ejemplo en una aplicación web luego hay una como campanita de notificaciones, lo cual me piden hacer algo así, la verdad no tengo idea de como hacerla ya que soy nuevo en esto, no se si el código que nos compartes pueda ayudarme ?

    Espero puedas apoyarme

    Saludos

    • Hola amigo! Estoy en el mismo dilema que tú… pero no logro hacerlo… tú cómo vas ya lograste resolver este detalle?

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad