Angular 2/4 - Material Design Snackbars按顺序排列多条消息

时间:2017-11-21 09:44:01

标签: javascript angular angular-material

我实施了一个“小吃栏服务”,显示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);
    }
}

我想按顺序显示多个零食吧:

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');
   }
}

但是所有消息都同时显示(重叠)。

如何等待snackBar afterDismissed向messageQueue显示新消息?

7 个答案:

答案 0 :(得分:5)

正如@Aamir Khan指出的那样 - 使用afterDismissed,我稍微调整了你的代码。

  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();
  });
}

add()里面添加了这个 -

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

Plunker

警告 - 由于使用了标志,上面的代码可能会有一些内存泄漏和竞争条件

答案 1 :(得分:2)

你需要使用超时方法。 在特定时间隐藏零食栏并使用超时功能打开另一个零食吧

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

答案 2 :(得分:2)

这是这里的@ Ankit's Plunker。

import {Subscription} from 'rxjs';
import {Injectable, OnDestroy} from '@angular/core';
import {MatSnackBar, MatSnackBarConfig} from '@angular/material/snack-bar';
import {MatSnackBarRef, SimpleSnackBar} from '@angular/material/snack-bar';
import {SnackBarMessage} from './snackBarMessage.model';

@Injectable()
export class NotificationService implements OnDestroy {
  private messageQueue: Array<any> = Array<any>();
  private subscription: Subscription;
  private snackBarRef: MatSnackBarRef<SimpleSnackBar>;
  private isInstanceVisible = false;

  constructor(public snackBar: MatSnackBar) {}

  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.
   * @param classOverride Adds a css class on the snackbar so you can add color.

   */
  show(
    message: string,
    action?: string,
    config?: MatSnackBarConfig,
    classOverride: string = 'blue-snackbar'
  ): void {
    if (!config) {
      config = new MatSnackBarConfig();
      config.duration = 3000;
      config.verticalPosition = 'bottom';
      config.horizontalPosition = 'end';
      config.panelClass = [classOverride];
    }

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

    this.messageQueue.push(sbMessage);

    if (!this.isInstanceVisible) {
      this.showNext();
    }
  }

  private showNext() {
    if (this.messageQueue.length === 0) {
      return;
    }

    const message = this.messageQueue.shift();
    this.isInstanceVisible = true;

    this.snackBarRef = this.snackBar.open(
      message.message,
      message.action,
      message.config
    );

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

答案 3 :(得分:1)

您可以通过进行以下简单的更改来实现此目的:

this.snackBarService.add(['A','B','C']); // pass messages as array

add(messages: Array<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;

    messages.forEach((message,index) => {
        setTimeout((message) => {
            sbMessage.message = message;
            this.messageQueue.next(sbMessage);
        },(config.duration*index);
    })
}

答案 4 :(得分:1)

这是我的解决方案


#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
 
int main(){
    int a, b, i, j,k,l;
    int array1[i][j], array2[i][j];
    
    
    printf("enter Matrix line number: ");
    scanf("%d", &a);
    printf("enter the number of Matrix columns:");
    scanf("%d", &b);
    
    int matrix[a][b], matrix2[b][a];
    
    for(i=0; i<a; i++) 
    {
        
    }
      for(int i=0; i<a; i++)
        for(int j=0; j<b; j++){
            array1[i][j] = rand() % 10+1; 
            array2[i][j] = rand() % 10+1; 
            
        }
    printf("\nOur matrix:\n");
    for(int i=0; i<a; i++){
        for(int j=0; j<b; j++){
            printf("%3d ", array2[i][j]);
        }
        printf("\n");
    }
    
    printf("select a cell from The Matrix:\n");
    printf("Senter the line of The Matrix you are selecting:\n");
    scanf ("%d",&k);
    printf ("enter the column of The Matrix you are selecting:\n");
    scanf("%d",&l);
    int p=array2[k][l];
    int r=array2[k-1][l];
    int m=array2[k][l-1];
    int n=array2[k+1][l];
    int s=array2[k][l+1];
    if(p>r && p>n && p>s && p>m)
    {
        
        printf("The cell you enter is larger than all its neighbors.\n");
        char S=array2[k][l];
        S='R';
        printf("%3d", array2[k][l]);
    }
    else
    {
        printf("The cell you enter is smaller than its neighbors and / or has a value equal to its neighbors.\n");
        char S=array2[k][l];
        S='G';
        printf("%3d", array2[k][l]);
    }
    getch();
    return 0;
}

答案 5 :(得分:0)

邮件列表:

messages = [
  {
    text: "This is message 1",
  },
  {
    text: "This is message 2",
  }

];

循环浏览它们并显示一条消息

message.forEach( (message, index) => {

        setTimeout(() => {

            this.snackBar.open(message.text, action, {
                duration: this.timeOut,
                verticalPosition: 'bottom', // 'top' | 'bottom'
                horizontalPosition: 'end', //'start' | 'center' | 'end' | 'left' | 'right'
                panelClass: [className],
            });


        }, index * (this.timeOut+500)); // 500 - timeout between two messages

    });

演示:

https://www.coditty.com/code/angular-material-display-multiple-snackbars-messages-in-sequence

答案 6 :(得分:0)

面对同一问题和另一个要求,当所有snackbar关闭时,需要通知调用函数,我终于使用递归使用了该解决方案:

调用方法:

testFunction(): void {
    this.showMsgs().subscribe(() => {
        // Do stuff after all messages were closed
    }
}

该函数一个接一个地显示所有消息,并让调用者函数关闭所有Messenger:

showMsgs(): Observable<any> {
    if (this.msgList.length) {
        const msg = this.msgList.pop();
        let closedEvent: Observable<any>;
        // If there are other msgs they should be shown first
        if (this.msgList.length) {
            closedEvent = this.showMsgs().pipe(
                switchMap(() => {
                    return this.snacBar.open(msg, 'Ok', {
                        verticalPosition: 'top' }).afterDismissed();
                }));
        } else { 
            // This is the only msg on the list, show it and return the close subscription
            closedEvent = this.snacBar.open(msg, 'Ok', {
                verticalPosition: 'top' }).afterDismissed();
        }
        return closedEvent;
    }