Luiz Ricardo Cardoso
Luiz Ricardo Cardoso

Reputation: 1624

How to show several snackbar without overlapping

Can I display multiple messages on the screen without overlapping others with snackBar?

I have a service to display messages in my application, but the problem is that when more than one event occurs that I need to display the message on the screen, the message is overwritten by the last message to be displayed.

I need a good way of not overlapping my messages and displaying one underneath the other without replacing the others.

I would like it to work the same way a Toast works, displaying one message underneath the other without overlapping.

The way I'm doing it below, only displays one message at a time on the screen.

snack-message.service.ts:

  horizontalPosition: MatSnackBarHorizontalPosition = 'center';
  verticalPosition: MatSnackBarVerticalPosition = 'top';

  constructor(
    public snackBar: MatSnackBar){}

  showMessage(message: string) {
    this.snackBar.open(message, 'Close', {
      duration: 5000,
      horizontalPosition: this.horizontalPosition,
      verticalPosition: this.verticalPosition,
    });
  }

Upvotes: 11

Views: 21059

Answers (2)

Alex Andreychuk
Alex Andreychuk

Reputation: 61

I was able to to organize message queue within one snackbar. However, visually messages look pretty independent. Particularly, each message has its own close button.

Here is how it looks like (for demonstration purposes I initiated the message queue that generates a new message every 2 seconds and lifetime 5 seconds):

entrypoint.ts

constructor(private notificationService: NotificationService) {}  
    
testNotifications(): void {
    setInterval(() => {
        this.notificationService.pushNotification(
            `Hello there! This is message ${this.index++}`, 5000);
    }, 2000)
}

notification.service.ts

import {Injectable} from "@angular/core";
import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar";
import {NotificationComponent} from "../notification/notification.component";

@Injectable({
    providedIn: 'root',
})
export class NotificationService {
    private messages: string[] = [];
    private snackBarRef: MatSnackBarRef<NotificationComponent>;
    private snackBarIsDisplayed: boolean = false;

    constructor(private snackBar: MatSnackBar) {
    }

    public pushNotification(message: string, duration: number = 5000): void {
        this.messages.push(message);
        if (!this.snackBarIsDisplayed) {
            this.snackBarRef = this.snackBar.openFromComponent(NotificationComponent, {
                horizontalPosition: 'end',
                verticalPosition: 'top',
                data: {
                    messages: this.messages,
                    duration: duration,
                }
            });
            this.snackBarIsDisplayed = true;
        }
        setTimeout(() => this.snackBarRef.instance.removeMessage(message), duration);

        this.snackBarRef.afterDismissed().subscribe(() => {
            this.snackBarIsDisplayed = false;
        });
    }
}

notification.component.ts

import {Component, Inject} from '@angular/core';
import {MAT_SNACK_BAR_DATA, MatSnackBarRef} from "@angular/material/snack-bar";

@Component({
    selector: 'app-notification',
    templateUrl: './notification.component.html',
    styleUrls: ['./notification.component.css']
})
export class NotificationComponent {

    messages: string[] = [];

    constructor(public snackBarRef: MatSnackBarRef<NotificationComponent>,
                @Inject(MAT_SNACK_BAR_DATA) public data: any) {
        this.messages = this.data.messages;
    }

    removeMessage(message: string) {
        this.messages.splice(this.messages.indexOf(message), 1);
        if (this.messages.length === 0) {
            this.snackBarRef.dismiss();
        }
    }
}

notification.component.html

<div *ngFor="let message of messages" class="custom-snack-bar d-flex flex-column align-items-center">
    <div class="custom-snack-bar d-flex flex-row align-items-center">
        <mat-icon>error</mat-icon>
        <span class="message-container">{{ message }}</span>
        <button mat-icon-button (click)="removeMessage(message)" color="accent">
            <mat-icon>close</mat-icon>
        </button>
    </div>
</div>

Output looks this way: notifications-queue

Upvotes: 3

Rafael Ferreira
Rafael Ferreira

Reputation: 186

I think the Snack Bar is only possible to use once.

Check the documentation:

https://material.io/components/snackbars#usage

Only one snackbar may be displayed at a time.

Upvotes: 12

Related Questions