DEV Community

Henri de la Hoz
Henri de la Hoz

Posted on • Edited on

Implementing and consuming a notification service

In this post we are going to see one way we can implement a notification Angular service that can be used in different pages or use cases in our app.

What is needed before working in our service?

  • This example was done in Angular 14 and RXjs 6

  • We need to create a shared module that is going to expose our notification component. Remember that a shared module is a module that is included in the file app.module.ts when we do that, we are making the module available all over our app.

import { SharedModule } from './shared/shared.module';

@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        AppRoutingModule,
        SharedModule,
        BrowserAnimationsModule,
    ],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Create the service via CLI

The first step is simply generating our service by the following command

ng g s shared/services/notification
Enter fullscreen mode Exit fullscreen mode

you can place the service wherever you want, in my case I chose the shared folder because the service follows the principle of being available to other modules in the app.

Implementing notification service

For the sake of Typescript principles, we will use an interface for typing the notification object that is going to "travel" through our service.

export enum NotificationType {
  success = 'is-success',
  danger = 'is-danger',
  warning = 'is-warning',
}

export interface MaNotification {
  title: string;
  message: string;
  type: NotificationType;
}
Enter fullscreen mode Exit fullscreen mode

Simply, our notification object will have a title, a message and a type.

Now let's import our notification interface and implement our service.

import { Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs';
import { MaNotification } from 'src/app/models/notification.model';

@Injectable({
    providedIn: 'root',
})
export class NotificationService {
    private notifyRequest = new ReplaySubject<MaNotification>();

    notifyRequest$ = this.notifyRequest.asObservable();

    constructor() {}

    notify(notification: MaNotification) {
        this.notifyRequest.next(notification);
    }
}
Enter fullscreen mode Exit fullscreen mode

So, what do we see here?

  1. We see the annotation Injectable that has the attribute providedIn, this setting is allowing us to use our service all over the app.

  2. We notice that we have a private attribute named notifyRequest, this is a ReplaySubject object (a ReplaySubject is an observable and it emits/send values to the observers subscribed to it)

  3. we can also see another attribute and this one is not private. this attribute is an observable that is going to be used by the notification component to react when a new value is received (we will see this later).

  4. finally, we see a notify method. This method is going to be used by the service consumers that are interested in display a notification (we will see this later).

Create the component via CLI

Obviously, a notification needs to be visual. So, we create our component as follows

ng g s shared/component/notification
Enter fullscreen mode Exit fullscreen mode

Once the component is generated, we need to make it available to the consumers (other pieces of the app like components, etc.). We do that by adding our component to the exports array in our shared module as follows

import { NotificationComponent } from './components/notification/notification.component';

@NgModule({
    declarations: [NotificationComponent,],
    // providers, imports go here
    exports: [NotificationComponent],
})
export class SharedModule {}
Enter fullscreen mode Exit fullscreen mode

Of course, your shared module will contain more stuff / code. Here I am just focusing the code on what is connected to our notification implementation.

Before implementing a our component, remember the enum we created previously NotificationType. The values of such enum fit the classes from material design css library.

Now we will implement the component logic as follows

import { Component, OnInit } from '@angular/core';
import { debounceTime, map, tap } from 'rxjs/operators';
import {
    MaNotification,
    NotificationType,
} from 'src/app/models/notification.model';

//Importing our notification service
import { NotificationService } from '../../services/notification/notification.service';

@Component({
    selector: 'ma-notification',
    templateUrl: './notification.component.html',
    styleUrls: ['./notification.component.scss'],
})
export class NotificationComponent implements OnInit {
    //Flag that is going to be used in the view to determine if the notification should be visible or not.
    showNotification: boolean = false;

//Notification object with the data that is going to be showed.
    incommingNotification: MaNotification = {
        title: '',
        message: '',
        type: NotificationType.danger,
    };

    constructor(private notificationService: NotificationService) {}

    ngOnInit(): void {
       //We subscribe or listens to new values / notification requests.
        this.notificationService.notifyRequest$
            .pipe(
//we receive new notification and update the values of the notification object we have in this component.
// we alse make the notification visible.
                tap((notification: MaNotification) => {
                    this.incommingNotification = notification;
                    this.showNotification = true;
                }),
//we wait for 3 seconds before updating the visibility of the notification
                debounceTime(3000),
//3 seconds later, we make our notification invisible again and ready for the value.
                tap(() => {
                    this.showNotification = false;
                })
            )
            .subscribe();
    }
Enter fullscreen mode Exit fullscreen mode

Perfect, now let's see the visual part of our component.

<div
    [class.is-visible-notification]="showNotification"
    class="ma-notification"
    [class]="incommingNotification.type"
>
    <h3 class="ma-notification-title">
        {{ incommingNotification.title }}
    </h3>
    <p class="ma-notification-paragraph">{{ incommingNotification.message }}</p>
</div>
Enter fullscreen mode Exit fullscreen mode

Simple, right?

By means of a css class we control the visibility, and you can also see that incommingNotification.type set the value of a class, such value fits with a Material Design built-in class and in that way, we set the color. If you don't use MD you just need to set css classes that set the color to the div (a "danger" class, "warning" class, etc.).

But how to consume this service?

We will consume it in a hypothetic login component page

import { Component, OnInit } from '@angular/core';

import { NotificationType } from 'src/app/models/notification.model';
import { NotificationService } from 'src/app/shared/services/notification/notification.service';
import { LoginRequest } from '../../../shared/services/user/user.model';
import { UserService } from '../../../shared/services/user/user.service';

const { home } = environment.pages;

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit {
    constructor(
        private notificationService: NotificationService
    ) {
    }

    ngOnInit(): void {}

    async handleSubmit(loginRequest: LoginRequest): Promise<void> {
        const signInRes = await this.userService.authenticate(loginRequest);
        if (signInRes.error) {
            this.notificationService.notify({
                title: 'Oh Oh 😕',
                type: NotificationType.danger,
                message: signInRes.error.message,
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)