How can I implement a user notification service using rxjs within Angular?

As a newcomer to reactive programming, I am exploring ways to create an Angular service that can present notifications to the user. Check out what I have accomplished so far:

https://stackblitz.com/edit/angular-rxjs-notifications?file=app%2Fapp.component.html

The main challenge in my implementation lies in figuring out how to queue notifications in a reactive manner. My goal is for the notification div to display when the first notification is pushed, and disappear only when "Clear" is clicked unless more notifications have been added since. This way, clearing would reveal the next notification, continuing until all notifications are cleared. Subsequently, once a new notification is received, the div should reappear.

In my setup, I opted for a Subject instead of a ReplaySubject because I do not want users to see notifications sent while they were loading the next screen. However, I realized that if there is routing within my app.component.html, this behavior may still occur. Perhaps I need to clear the notification queue upon navigation?

Your insights are greatly appreciated!

Answer №1

Here is an example showcasing the structure of a service:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { merge } from 'rxjs/observable/merge';
import { map, scan } from 'rxjs/operators';

enum ActionType {
  insert = 'insert',
  remove = 'remove'
}

interface Action {
  type: ActionType;
}

interface InsertAction extends Action {
  payload: string;
}

interface RemoveAction extends Action { }

@Injectable()
export class NotificationService {
  messages$: Observable<string[]>;

  private insertSource = new Subject<string>();
  private removeSource = new Subject<void>();

  constructor() {
    const insert$ = this.insertSource.asObservable()
      .pipe(map((payload) => ({ type: ActionType.insert, payload })));

    const remove$ = this.removeSource.asObservable()
      .pipe(map((payload) => ({ type: ActionType.remove })));

    this.messages$ = merge(insert$, remove$)
      .pipe(
      scan((acc: any, { payload, type }) => {
        if (type === ActionType.remove) {
          acc = acc.slice(0, -1);
        }
        if (type === ActionType.insert) {
          acc = [...acc, payload]
        }
        return acc;
      }, [])
      );
  }

  insertMessage(msg: string) {
    this.insertSource.next(msg)
  }

  removeMessage() {
    this.removeSource.next()
  }
}

Check out the live demo here

We utilize two streams, remove$ and insert$, merging them together before reducing to a singular array through the scan operator.

For instance, a sequence like

insert(hello) -> insert(world) -> remove -> insert(kitty)
would be condensed down to [hello, kitty].

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Leveraging Angular 4-5's HttpClient for precise typing in HTTP requests

Utilizing a helper service to simplify httpClient calls, I am eager to enforce strong typing on the Observable being returned. In my service where I utilize the api Service and attempt to obtain a strongly typed observable that emits: export class ApiU ...

Angularfire2: Access Denied Error When User Logs Out

When utilizing the following method: login() { this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider()) .then(() => { this.router.navigate(['']); }); } An error occurs during logout: zone.js:915 Unca ...

Endure the class attribute in Angular 5

My SearchComponent has a route (/search) and SearchDetailComponent has a route (/search-detail:id). In the SearchComponent, there is a searchbox (input field) where I can type any text to start a search. After loading the search results and navigating to ...

I encountered an error in my Spring Boot application where I received a TypeError: Cannot read properties of undefined (reading '0') related to the angular.min.js file at line 129

As I work on designing a login page using Angular in my Java Spring Boot application, I encounter an issue. When attempting to log into the panel page, the username and password are successfully sent to the application via the API controller and the user t ...

Selected Angular Radio Button

Back in the good ole days of basic HTML and CSS, I was able to achieve the following: input:checked+label { background-color: #f00; } <div class="col-xs-6"> <input type="radio" id="template-1" name="template" value="template1" checked> ...

Vitest surpasses Jest by providing explicit type declarations, ensuring no more "unknown type" errors

Transitioning from Jest to vitest has been a smooth process for me. I'm currently in the midst of converting the following code snippets: // Jest const myLib = jest.requireActual("mylib.js") to this: // Vitest const myLib = await vi.importA ...

When running the 'npm install' command, it automatically downloads and installs the most recent versions of the libraries, regardless of the versions specified in the package.json file

When I tried to download the dependencies for my Angular project, I used the npm install command in the command prompt at the project level folder. To my surprise, it seems like this command is installing the latest versions of the libraries instead of the ...

Step-by-step guide on implementing a draggable component for selecting the year using React

I am looking to develop a draggable component in React without relying on any third-party library. Below, I have provided an image depicting how the component might look. Currently, my component code appears as follows: import React from 'react'; ...

Angular Material Spinner with Custom Image Icons - (mat-spinner)

I have successfully implemented the mat-spinner in my project with configurable changes like color and mode of spinning. However, I am now looking to add an image icon, specifically the logo of a brand or company, inside the spinner. How can I achieve this ...

Displaying a pair of items side by side using Angular's ngFor

I've encountered an issue with my code related to displaying a 4 by 8 grid using the ngFor command. Below is the snippet of the code in question: <div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> <ng-cont ...

Tips for extracting a keyword or parameters from a URL

I'm in the process of creating my personal website and I am interested in extracting keywords or parameters from the URL. As an illustration, if I were to search for "Nike" on my website, the URL would transform into http://localhost:3000/searched/Nik ...

What is the process for including a new item in the p-breadcrumb list?

Having trouble getting my code to add a new item to the p-breadcrumb list on click. Any assistance would be greatly appreciated. Thank you in advance! Check out the live demo here ngOnInit() { this.items = [ {label: 'Computer'}, ...

Tips for sorting the mat table dataSource by inputting two columns and selecting the search button

I am currently displaying mat table data with columns for Role, Status, Email ID, and Name. Above the table, there is a search area where users can enter values for Role and Status and then click the Search button. If the entered values match exactly for ...

The process of sorting through an array of objects based on their specific types in TypeScript

I am working on a function that filters an array of objects based on their type property: export const retrieveLayoutChangeActions = (data: GetOperations['included']) => data.filter(d => d.type === 'layoutChangeAction') as Layou ...

Creating custom TypeScript validation types at compile time

Is it possible to create custom type definitions in TypeScript that are only checked during compile time? I want users to define a value for a variable (that won't change at runtime) and validate if it meets certain criteria. For example, requiring a ...

Accessing an Excel file in TypeScript using the .xlsx format

After extensive research, I managed to find a solution for reading the .xlsx file in a TypeScript environment. Once implemented, I documented the solution along with a question and answer. The file "demo.xlsx" contains UserIds and Code, displayed in the i ...

The Sanity npm package encounters a type error during the build process

Recently, I encountered an issue with my Next.js blog using next-sanity. After updating all npm packages, I found that running npm run build resulted in a type error within one of the dependencies: ./node_modules/@sanity/types/lib/dts/src/index.d.ts:756:3 ...

Consistentize Column Titles in Uploaded Excel Spreadsheet

I have a friend who takes customer orders, and these customers are required to submit an excel sheet with specific fields such as item, description, brand, quantity, etc. However, the challenge arises when these sheets do not consistently use the same colu ...

Experimenting with HttpClient request using callFake() method

I am currently facing a challenge while trying to devise a spec for testing a method within my Angular service that initiates a GET request. The main issue I'm encountering is how to simulate the method returning an error instead of the expected respo ...

Should we implement REST API with authentication?

I am seeking guidance on building an application from scratch and I have encountered some challenges. The plan is to create a front-end using Angular and a backend that will communicate via REST API. This application will be deployed on individual devices, ...