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

5.4k 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 answers

2
MoxxiManagarm 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
Benzara Tahar 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
Gunnar B. 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
Daniel 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.