Route "beforeLeave" hook and the "routeFrom" page component data/methods?

Hi there,

Doc Page Reference: https://framework7.io/docs/routes.html#route-before-enter-leave

I was reading the example in the above linked docs involving the beforeLeave hook to display a dialog before a user leaves a page without saving.

I understand that in the scope of the beforeLeave function, “this.app” is the Framework7 $app object. So I can call $app form methods to check the dom form data here but… I already have a “checkForChanges” method in the page component involved in the FROM route so I wanted to re-use that in this route method.

I solved this in the attached condensed code below, but I wanted to get thoughts on if this is supported, recommended, etc…

route.js code
routes: [
  {
    path: '/vendors/:vendorId?/',
    component: VendorsPage,
    beforeLeave: function (routeTo, routeFrom, resolve, reject) {
		var router= this;
		var $app= router.app;

		// This next line does not work because the component referenced in the routeFrom argument (routeFrom.route.component) is just the definition, not the live mounted component.
		// var changesMade= routeFrom.route.component.methods.checkForChanges();
		// So this is how I referenced the live mounted component.
		var routeFromMountedComponent= router.currentPageEl.f7Component;
		var changesMade= routeFromMountedComponent.checkForChanges.bind(routeFromMountedComponent)();

		// checkForChanges returns an object of the changes, so check if it's not empty...
		if ((changesMade) && (Object.keys(changesMade).length > 0)) {
			$app.dialog.confirm('Are you sure you want to leave this page without saving?',
				 function () {
				     // proceed navigation
				      resolve();
				 },
				  function () {
				     // stay on page
				      reject();
				 }
			);
		} else {
			 resolve();
		}
    }
  }
]

As I said, the code above works because I am referencing the mounted FROM component and call a local method with a BIND to make sure the “this” scope is accurate in the local method.

@nolimits4web, is there a better way to do this? Do you see any issues in doing it this way?

I would like to keep my routes file as generic as possible and have all the logic for each page in the specific component/page.

Thanks,

  • Matt

PS: Should’ve mentioned I using the latest Core F7 [v5.7.13]

I think, where you need such check you can create component’s method with the name beforeLeave. Then you can use “global” beforeLeave callback by specifying it as View’s routesBeforeLeave method. And in this callback check if current route component has beforeLeave method.

app = new Framework7({
  //  ...
  view: {
    routeBeforeLeave(to, from, resolve, reject) {
      var router = this;
      var routeFromMountedComponent= router.currentPageEl.f7Component;
      if (routeFromMountedComponent.beforeLeave) {
        var rejected = routeFromMountedComponent.beforeLeave();
        if (rejected) reject();
        else resolve();
      }
      else resolve();
    }
  },
})

And keep your routes clean

That makes perfect sense. I will go that route.

Thank you for all you do with this framework!

  • Matt

Hi all, What “router.currentPageEl.f7Component” should be in Framework7 Vue ?

No, it is only in Core version

I found the solution in framework 7 Vue,
In route.js


    function beforeLeave(to, from, resolve, reject) {
      let beforeLeave = this.currentPageEl.__vue__.$parent.beforeLeave;
      if (beforeLeave && beforeLeave instanceof Function) {
        beforeLeave()
          .then(() => {
            resolve();
          })
          .catch(() => {
            reject();
          });
      } else {
        resolve();
      }
    }
    export default [
      {
        path: "/drawer/:drawerId?",
        asyncComponent: () => import("./assets/vue/pages/drawer.vue"),
        beforeLeave: beforeLeave,
      },
    ];

In drawer.vue:


    export default {
      methods: {
        beforeLeave() {
          return new Promise((resolve, reject) => {
            if (!this.modified()) {
              resolve();
              return;
            }
            this.$f7.dialog.prompt(
              "",
              "Save data before exit ?",
              (e) => {
                //save data
                resolve();
              },
              (e) => resolve(),
              'defaultValue'
            );
          });
        },
      },
    };