Is there a way to incorporate an AlertService (specifically mat snackbar for displaying error messages) within a different service?

I'm facing a challenge where I want to subscribe to an observable within a service. The catch is, I also need to utilize the AlertService to display error messages. Essentially, I have a service within another service, which seems to be causing a circular dependency issue.

Let's take a closer look at the AlertService:

@Injectable()
export class AlertService {
  private subject = new Subject<any>();
  private keepAfterNavigationChange = false;

  constructor(private router: Router) {
    router.events.subscribe(event => {
      if (event instanceof NavigationStart) {
        if (this.keepAfterNavigationChange) {
          this.keepAfterNavigationChange = false;
        } else {
          this.subject.next();
        }
      }
    });
  }

  success(message: string, keepAfterNavigationChange = false) {
    this.keepAfterNavigationChange = keepAfterNavigationChange;
    this.subject.next({ type: 'success', text: message });
  }

  error(message: string, keepAfterNavigationChange = false) {
    this.keepAfterNavigationChange = keepAfterNavigationChange;
    this.subject.next({ type: 'error', text: message });
  }

  getMessage(): Observable<any> {
    return this.subject.asObservable();
  }
}

The AlertService is transformed into a Mat Snackbar on the AlertComponent. The snackbar is then rendered on various other components.

export class AlertComponent implements OnInit {
  message: any;

  constructor(private alertService: AlertService, public snackBar: MatSnackBar) { }

  ngOnInit() {
    this.alertService.getMessage().subscribe(message => {
      if (message != null) {
        if (message.type === 'error') {
          this.snackBar.open(message.text, undefined, { duration: 8000, verticalPosition: 'bottom', panelClass: ['snackbar-error'] });
        } else if (message.type === 'success') {
          this.snackBar.open(message.text, undefined, { duration: 8000, verticalPosition: 'bottom', panelClass: ['snackbar-success'] });
        } else {
          this.snackBar.open(message.text, undefined, { duration: 8000, verticalPosition: 'bottom' });
        }
      }
    });
  }

}

I have successfully subscribed inside components like this:

export class AboutComponent implements OnInit {
  
  ngOnInit() {
    this.emailService.sendEmail('example@example.com')
      .subscribe(code => {
          console.log(code);
          this.alertService.success('Thanks for your message!');
      }, error => {
        this.alertService.error('Error sending message.');
      }
    );
  }
      
}

@Injectable()
export class EmailService {

    constructor(private http: HttpClient) { }

    sendEmail(email: Email) {
        return this.http.post(BACKEND_URL + 'send', email);
    }
}

However, I am now attempting to subscribe inside the service because EmailService will be utilized in multiple components. How can I achieve this desired functionality?

Answer №1

Inject your service into other services

@Injectable()export class EmailService {

constructor(private http: HttpClient, private alertService: AlertService) { }

sendEmail(email: Email) {
    return this.http.post(BACKEND_URL + 'send', email).map( result => this.alertService.alert(result););


  }
}

Attempting to have AlertService use EmailService and EmailService use AlertService would create a circular dependency

Answer №2

One issue with the current implementation is that if there are multiple alerts, the displayed alert gets dismissed before it can be read. I have made some adjustments to allow for the display of multiple alerts simultaneously.

export class ModifiedAlertService {
  
  private messageSubject = new ReplaySubject<any>();
  private showSubject = new BehaviorSubject<any>(1);
  
  public alert = zip(this.messageSubject.asObservable(), this.showSubject.asObservable());

  success(message: string) {
    this.messageSubject.next({ type: 'success', text: message });
  }

  error(message: string) {
    this.messageSubject.next({ type: 'error', text: message });
  }

  showNextAlert() {
    this.showSubject.next(1);
  }
}

In the AlertComponent, the code looks like this:

    ngOnInit(): void {  
    
    this.modifiedAlertService.alert.subscribe(([message, show]) => {
      if (message && message.text) {
        if (message.type === 'error') {
         this.snackbarRef  =  this.snackBar.open(message.text, 'Ok', { verticalPosition: 'top', panelClass: ['snackbar-error'] });
        } else if (message.type === 'success') {
          this.snackbarRef = this.snackBar.open(message.text, undefined, { duration: 3000, verticalPosition: 'top', panelClass: ['snackbar-success'] });
        } else {
          this.snackbarRef = this.snackBar.open(message.text, 'Ok', { duration: 3000, verticalPosition: 'top' });
        }   
        this.snackbarRef.afterDismissed().subscribe(()=>this.modifiedAlertService.showNextAlert());
      }
      else this.modifiedAlertService.showNextAlert();
    });    
  }

The zip operator is utilized to manage the timing of the next alert being displayed.

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

Strategies for handling asynchronous requests and effectively presenting the retrieved data

On my HTML page, I have a badge component that shows the number of unread messages. Here is the HTML code: <button class="font" mat-menu-item routerLink="/message"> <mat-icon>notifications</mat-icon> <span [matBadgeHidden]="newM ...

Issue encountered when working with interface and Observable during the http parsing process

Visual Studio File Structure Error app(folder) -->employee-list(folder) -->employee-list.component.html -->employee-list.component.ts -->app.component.html -->app.component.ts -->app.module.ts -->employee.json ...

Updating color of an element in SVG and Angular2+ according to the background

In my svg element, I have a text element positioned after two rect elements. <svg id="floor-plan" width="300" height="100"> <rect width="300" height="100" fill="white"/> <rect width="50" height="50" fill="green"/> <text x="10" y="10" ...

Is it possible to bind parameters in the select clause using TypeORM?

I'm currently working on implementing a search feature using the pg_trgm module in my PostgreSQL project built with TypeScript and TypeOrm. My SQL query that works for me looks like this: SELECT t, similarity(t, 'word') AS sml FROM test_t ...

Troubleshooting Firebase signInWithPopup issue in an Angular Chrome extension

Currently in the process of creating a Chrome extension using Angular and AngularFire. Encountering an issue where the signInWithPopup method is causing the authentication to fail as the popup closes automatically upon signing in. Aware that artificially ...

Error: Oops! The super expression can't be anything other than null or a function in JavaScript/TypeScript

I am facing an issue with class inheritance in my code. I have a class A that extends class B, which in turn extends class C. Whenever I try to create a new instance of class A within a function, I encounter the following error message: Uncaught TypeError: ...

Adding markers to a map in Angular 2 using ngOnInit after initialization

Embarking on my Angular journey by creating a sample app incorporating GoogleMaps. import { Component, Input, OnInit, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { FormControl } from '@ ...

Navigating with Conditions in Angular 2

I have two main components in my Angular application - Home and Login. The Login component serves as the default page, and once a user logs in, I want them to navigate to the Home component. Within my app.router.ts file, the routing configuration is set u ...

Asynchronous requests from clients paired with server-side rendering

Exploring the realm of SEO with Angular4/Node.js has presented a unique challenge for me. Utilizing Angular Universal allows for server-side rendering, enabling me to inject meta keywords, title, and image URLs into the HTML before it reaches the browser. ...

A guide on utilizing portals in Next.js to move a child element beyond its direct parent container

Current Setup Wrapper export const ContainerComponent = () => { return (<ChildComponent/>); } Child Component export const ChildComponent = () => { return ReactDOM.createPortal( <aside> <div>{"I am a c ...

Too many open files error encountered in Watchpack (watcher) - NextJS

Encountering an issue with watchpack resulting in the error messages shown above while running a next app using next dev. The error message is displayed continuously on the screen as follows: Watchpack Error (watcher): Error: EMFILE: too many open files, w ...

The error message states that the type '{}' does not contain the property 'API_BASE_URL'

Encountering this error message when trying to access my API_URL as defined in the enviroment.ts file within a service class. Error: src/app/product/product.service.ts:12:25 - error TS2339: Property 'API_BASE_URL' does not exist on type '{} ...

angular2-mdl encountered a 404 error and could not be located

I have encountered a strange 404 error stating that the page is not found. Despite installing angular2-mdl using npm install angular2-mdl --save and confirming its presence in the node_modules directory, the error persists. Below is a snippet from my app. ...

Tips for incorporating Material UI Icon v1.0.0-beta.36 into a .tsx component

Currently utilizing material-ui-icons v1.0.0-beta.36. I am endeavoring to incorporate a Search icon within a .tsx component. .tsx component: import React, { Component, ReactElement } from 'react' import Search from 'material-ui-icons/Sear ...

Lazy-loaded modules in Angular that contain services provided within the module

Currently, I am facing a challenge with lazy-loaded modules and services that are provided in these modules. My folder structure looks like this: app -> featureModule1 (lazy loaded) -> featureModule2 (lazy loaded) -->services --->servi ...

Tips for dynamically updating the Header name based on backend data

I am currently utilizing ag-grid within my Angular application. Within the columnDefs array, I have defined headers for my grid. One of the headers is for a column labeled Amount. My goal is to make this header dynamic, so that if the currency value coming ...

Issue with Child Component Loading Once CUSTOM_ELEMENTS_SCHEMA is Added to App Module

One of my components, known as HostComponent, works perfectly fine when set as the startup component in my application. However, I decided to create a new module called AppModule and nested the host component within the app component: import { Component, ...

Struggling to display data from Firebase Database in dropdown menu using Angular

Despite my extensive search efforts online, including watching YouTube videos and enrolling in Udemy courses, I have not been able to find the solution to my issue. My goal is to take an observable retrieved from Firebase and populate it into a dropdown me ...

Error encountered in app.module.ts file of Angular 2 application

My friends and I are working on a big school project, creating a cool web app. Suddenly, I encountered some errors in my app.module.ts file that I haven't seen before. It's strange because they are showing up out of nowhere! The error: Error:( ...

Unable to properly display date formatting in AG-Grid using the Angular date pipe

Currently, I am utilizing ag-grid in conjunction with Angular 8. Within my table, there is a column where my intention is to exhibit dates in a concise format. In order to achieve this, I opted to utilize the Angular date pipe. However, it appears that the ...