[Fixed] Angular 2/4 – Material Design Snackbars multiple message in sequence

Issue

I have implemented a “snackbar service” that display a snackbar:

snackbar.service.ts

import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';
import { Inject, Injectable, OnDestroy } from '@angular/core';
import { MatSnackBar, MdSnackBarConfig } from '@angular/material/snack-bar';
import { MdSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';

export class SnackBarMessage  {
  message: string;
  action: string = null;
  config: MdSnackBarConfig = null;
}

@Injectable()
export class SnackBarService implements OnDestroy
{
    private messageQueue: Subject<SnackBarMessage> = new Subject<SnackBarMessage>();
    private subscription: Subscription;
    private snackBarRef:  MdSnackBarRef<SimpleSnackBar>;


    constructor(public snackBar: MatSnackBar){
        this.subscription = this.messageQueue.subscribe(message => { 
            this.snackBarRef = this.snackBar.open(message.message, message.action, message.config);
        });
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }

    /**
     * Add a message
     * @param message The message to show in the snackbar.
     * @param action The label for the snackbar action.
     * @param config Additional configuration options for the snackbar.
     */
    add(message: string, action?: string, config?: MdSnackBarConfig): void{

        if ( !config ){
            config = new MdSnackBarConfig();
            config.duration = 10000;
        }

        let sbMessage = new SnackBarMessage();
        sbMessage.message = message;
        sbMessage.action = action;
        sbMessage.config = config;

        this.messageQueue.next(sbMessage);
    }
}

I want display multiple snackbars in sequence:

test.component.ts

import { Component } from '@angular/core';
import { SnackBarService } from 'app/core/services/snackbar.service';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})
export class TestComponent {

   constructor(public snackBarService: SnackBarService) {
     this.snackBarService.add('A');
     this.snackBarService.add('B');
     this.snackBarService.add('C');
   }
}

But all message are displayed at same time (overlapping).

How can I wait for a snackBar afterDismissed for display a new message into messageQueue?

Solution

As @Aamir Khan pointed out – using afterDismissed, I have tweaked your code a bit.

  showNext() {
  if (this.msgQueue === 0) {
    return;
  }

  let message = this.msgQueue.shift();
  this.isInstanceVisible = true;
  this.snackBarRef = this.snackBar.open(message.message, message.action, {duration: 2000});
  this.snackBarRef.afterDismissed().subscribe(() => {
    this.isInstanceVisible = false;
    this.showNext();
  });
}

And inside add() added this –

this.msgQueue.push(sbMessage);
if (!this.isInstanceVisible) {
     this.showNext(); 
}

Plunker

CautionIts kind of a dirty and non standard way, not an ideal user experience (IMO), above code might have some memory leaks and race conditions, due to usage of flags.

Leave a Reply

(*) Required, Your email will not be published