Close dialog/bottomSheet when clicking browser back?



  • Is there a way of closing dialog or bottom sheet when a user clicks the back button (desktop and mobile browser)?

    Currently I’m doing this:

    boot file

    Vue.prototype.$a = {
        bottomSheet: function(opt) {
          const self = opt.parent;
          const sheet = BottomSheet.create(opt).onDismiss(() => {
            self.$router.history.stack[self.$router.history.index].params.$dialog = null;
          });
          self.$router.history.stack[self.$router.history.index].params.$dialog = sheet;
          return sheet;
        },
        dialog: function(opt) {
          const self = opt.parent;
          const dialog = Dialog.create(opt).onDismiss(() => {
            self.$router.history.stack[self.$router.history.index].params.$dialog = null;
          });
          self.$router.history.stack[self.$router.history.index].params.$dialog = dialog;
          return dialog;
        }
      };
    

    router file

    Router.beforeEach((to, from, next) => {
        // close any open dialog
        const dialog = get(from, "params.$dialog", false);
        if (!!dialog) {
          dialog.hide();
        }
    }
    

    But this has its downsides:

    1. It won’t work if there’s no going back (stack is 1)
    2. It will go back a route not just close the dialog


  • @codebreaker dialog has a hide method.

      ...
      data
         dialog: null
      ....
      methods
         someFunc
            this.dialog = this.$q.dialog...
      ...
      beforeDestroy
         ...
         this.dialog.hide()
    


  • That is correct and I am using it to close the dialog in router file. Although, I did complicate it a lot.

    In the end, this does not solve the main two problems:

    1. It won’t work if the parent component won’t be destroyed
    2. It will go back a route not just close the dialog

    I have improved upon my previous attempt that solve these issues:

    boot file

    
    function dialogHideLogic(dialog) {
      const hideDialog = () => dialog.hide();
    
      // listen for browser back button
      window.addEventListener("popstate", hideDialog);
      // add to current state a signal so that the router change routes
      window.history.replaceState({ ...window.history.state, freeze: true }, "", window.location.href);
      // add new state so that the back button will not go to previous route
      window.history.pushState({}, "", window.location.href);
    
      return dialog.onDismiss(() => {
        window.removeEventListener("popstate", hideDialog);
        // if hide isn't called by popstate event, go back one step to remove the freeze step
        if (!get(window.history.state, "popstate", false)) window.history.back();
      });
    }
    
    export default () => {
      Vue.prototype.$a = {
        bottomSheet: function(opt) {
          return dialogHideLogic(BottomSheet.create(opt));
        },
        dialog: function(opt) {
          return dialogHideLogic(Dialog.create(opt));
        }
      };
    };
    

    router file

      // hook into browser back button
      window.addEventListener("popstate", function(event) {
        if (get(event.state, "freeze", false)) {
          // clear freeze so that we can call this route again when user uses the browser back
          window.history.replaceState({ ...window.history.state, freeze: false }, "", window.location.href);
        } else {
          Router.back();
        }
        // don't allow user to go back since we can't handle browser forward (no event)
        window.history.pushState({ popstate: true }, "", window.location.href);
      });
    

    It’s a bit of a hack, but it works.

    Note: I am using abstract routing so that why I can do this. I don’t know if it can be done in other modes.



  • A few bug fixes later:

    boot file

    function dialogHideLogic(dialog) {
      const hideDialog = () => dialog.hide();
    
      // listen for browser back button
      window.addEventListener("popstate", hideDialog, { once: true });
      // append to current state a signal so that the vue router does not go back it's history stack
      window.history.replaceState({ ...window.history.state, freeze: true }, "", window.location.href);
      // push new state so when back button is pressed, window.history.state contains the freeze property
      window.history.pushState({ ...window.history.state, freeze: false }, "", window.location.href);
    
      return dialog.onDismiss(() => {
        // remove popstate listener
        window.removeEventListener("popstate", hideDialog);
        // push a fresh state onto history stack
        window.history.pushState({}, "", window.location.href);
      });
    }
    
    export default () => {
      Vue.prototype.$a = {
        bottomSheet: function(opt) {
          return dialogHideLogic(BottomSheet.create(opt));
        },
        dialog: function(opt) {
          return dialogHideLogic(Dialog.create(opt));
        }
      };
    };
    
    

    router file

      // hook into browser back button
      window.addEventListener("popstate", function(event) {
        if (!get(event.state, "freeze", false)) {
          Router.back();
        }
        // don't allow user to go back since we can't handle browser forward (no event, not that we want to)
        window.history.pushState({ popstate: true }, "", window.location.href);
      });
    

Log in to reply