Open several mat-dialogs one by one after the previous one is closed

5.5k Views Asked by At

In my code I have a button which will browse a list of data and open a mat-dialog for each data.

Unfortunately, during the course of the loop, all the mat-dialogs open.

What I would like to happen is that by using the dialogRef.afterClosed() method, depending on the result (true) the next mat-dialog opens or (false) the loop ends.

openDialog(): void {
  this.data.forEach(data => {
    const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
      disableClose: true
    });
    dialogRef.componentInstance.data = data;

    dialogRef.afterClosed().subscribe(result => {
      if (result) {
        // open next mat-dialog
      } else {
        // stop loop
      }
    });
  });
}

<div mat-dialog-actions>
  <button mat-button (click)="_dialogRef.close(true)">Ok</button>
  <button mat-button (click)="_dialogRef.close(false)">Cancel</button>
</div>

StackBlitz Here

How can I do this? I don't know how to go about it.

Thanks for your help.

4

There are 4 best solutions below

2
On BEST ANSWER

You can achieve this by rxjs takeWhile

from(this.data)
      .pipe(
        concatMap(x => {
          const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
            disableClose: true
          });

          dialogRef.componentInstance.data = x;

          return dialogRef.afterClosed();
        }),
        takeWhile(Boolean)
      ).subscribe();

See https://stackblitz.com/edit/open-mat-dialogs-one-by-one-after-the-previous-one-is-cl-yqprmt?file=src/app/dialog-overview-example.ts

0
On

You can achieve this by marking you method as async and awaiting your dialog afterClosed() call, but since awaitworks only with promises you need to convert the Observable to a Promise.

Here is a the solution that works for me

@Component({
  selector: "dialog-overview-example",
  templateUrl: "dialog-overview-example.html",
  styleUrls: ["dialog-overview-example.css"]
})
export class DialogOverviewExample {
  data: any[] = DATA;
  constructor(public dialog: MatDialog) {}

  async openDialog() {

  for(var i =0; i < this.data.length; i++) {
    const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
        disableClose: true
      });
      dialogRef.componentInstance.data = this.data[i];
      var result = await  dialogRef.afterClosed().toPromise();
        if (!result) {
          console.log("Stop loop", result);
          break;
        }
        // else continue the loop

   }
 }
}

Stackblitz Working Demo

0
On

I'd suggest to not use an iterator (foreach) (in fact, I would discourage using one), but instead trigger openDialog again in the subscribe if there is still more data to display (queue-like approach). Of course this requires to remove the shown data from the list, but it would also allow for new data being appended to it in the meantime.

0
On

Here's my take on the "queued dialogs service" on stackblitz. Calling the service's open method

  • will show the dialog, if the queue is empty or the dialog has a higher priority than the currently displayed one
  • will queue the dialog, if there is a dialog with higher priority already opened

There may be dragons along the way.