Universally close Angular Material's MatDialog modals with back button

I’m trying to close open dialog boxes (created with MatDialog) using the Android hardware back button.

There are several MatDialog boxes created on different pages and I’m trying to clear them with the back button, without navigating the app, just like how the device naturally handles JS alert() popups.

app.component.ts

backHandler() {
    const event = fromEvent(document, 'backbutton');

    event.subscribe(async () => {
      if (this.dialog.openDialogs.length > 0) {
        // If dialogs are open, close the top one
        this.dialog.openDialogs[this.dialog.openDialogs.length - 1].close();
      }
      else {
        this.routeState.popFromHistory();
        this.navCtrl.back();
      }
    });
  }

This code is successfully intercepting the back button, and openDialogs returns a list of any open modals.
When I run .close() on the last item in the array, it doesn’t return any errors, but the modal stays open. If I change my phone’s orientation, or exit and re-enter the app (i.e. force the app to redraw) THEN it closes the modal and triggers its ngOnDestroy() function.

The same thing happens if I just run this.dialog.openDialogs.closeAll().

I’m not sure what package versions or other code would be helpful to provide, but I can give whatever’s necessary.

Since this is an Angular Material component, and not an Ionic Framework one - you might consider opening an issue in that repo, the Angular subreddit, etc.

That said, since you’ve narrowed it down to an issue with the last open dialog, that’s something to work with. Your code looks correct, but could it be an array indexing problem? You’re accessing the length of openDialogs while also closing them, which affects the array and length. Try console logging the openDialogs object and the length etc - see if an error is silently thrown.

Ah, thanks for clarifying, there are a lot of different Angular components and sometimes it’s tough to tell what would be relevant to ask where.

I figured since the goal is to use the back button, and it’s affected by rotating or exiting/entering the app, it was tied closely enough to Ionic to post. Would there be a better tag here for it?

I’ve tried console.logging every relevant object I could think of, and I tried setting that specific MatDialogRef (the object type in the array) to a new variable in case that would be relevant. Still wind up with the strange case where it doesn’t close until the app’s redrawn, and I’m not sure how to manually trigger that.

No problem! The question is welcome here, just wanted to mention that it’s another UI library for clarity.

Unfortunately, I’m not very familiar with Material and since the issue is with that component’s close function, perhaps it’s an Angular Material bug? check the open/closed issues list, sometimes you can find answers there. Another idea is debugging the app, using breakpoints to step through the code. See this guide for different options.

It looks like it was an Angular Zone problem. I saw this solution for similar issues in my research earlier and I tried it out. I’m not sure what I did then that didn’t work, but this worked now.

      if (this.dialog.openDialogs.length > 0) {
        // If dialogs are open, close the top one
        this.ngZone.run(() => {
          this.dialog.openDialogs[this.dialog.openDialogs.length - 1].close();
        });
      }

And suddenly I’m able to close every modal across my whole app with the back button. Woohoo!

1 Like

Sorry to funpire, but manually messing around with zones is virtually never a good idea, as it is papering over a more serious condition that is very likely to leak out at some other time and in some other place. I am also very strongly of the opinion that this entire topic should be mooted by design. Modal dialogs are disruptive to workflow. They indicate that a particular task requires immediate user attention, and should be as simple as possible, containing just enough elements to complete that single task. Ergo, there shouldn’t be a situation where you have multiple modals open at once, which has the nice side effect of eliminating worries about how to close them all.

Yeah I saw your other posts in every other similar thread. I debated trying to pre-emptively address it, but the post was already getting a bit long.

It’s fine to suggest better design, but people don’t always have a say in the design of the product they’re trying to code. The users for this app aren’t tech-savvy, and will likely get confused if they were temporarily sent to a full new page for every setting and function of the app.

Modals aren’t always spontaneous alerts. In our case, the app displays a list of entities and the user can perform certain actions on them. To save screen space, certain events open modals. This says to the user “your list is still intact behind this, but you’ve requested a form or activity related to the list or an item on it.”

I think there are rare cases where it’s acceptable to have a dismissable alert in front of this. If the modal has a form, and the user enters invalid data, sometimes these users don’t notice which fields are highlighted. We’ve been trying to improve that, but have found it’s much more reliable with an alert telling them which fields are invalid. Urgent message in front of current action being formed on an item in a list. I think it’s a valid case.

Even if it isn’t a valid case, that’s still the design that was discussed with the client and their users and was approved. Not every project is done from the ground up, either. Perhaps I inherited a codebase that has layers of modals and the users are used to that workflow. The client’s probably not going to budget for a complete redesign of something in production, even if it goes against good design policy, so nothing is accomplished by completely dismissing the premise of the question.

Please keep in mind that support forums like this are knowledge archives, so your thread will also be read by other people. I’m writing to reach any of those who may be more persuadable on the topic, so I respectfully disagree that “nothing is accomplished”.

Fair point, and I’m glad a code solution was discussed and reached first.

Even if I feel there are exceptions to your design preference, I think it’s important to acknowledge that the design is controversial and why.