Reputation: 1624
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
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
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