Giter Club home page Giter Club logo

Comments (30)

yassilah avatar yassilah commented on April 28, 2024 25

[UPDATED for @vue/composition-api >= v1.0.0-beta.22]

I don't know if that's still relevant to anyone but I ended up making my own (very simple) composable function:

import Vue from 'vue'
import { getCurrentInstance } from '@vue/composition-api'
import { NavigationGuard } from 'vue-router'
import { ComponentOptions } from 'vue/types/umd'

export function onHook(
  name: keyof ComponentOptions<Vue>,
  callback: (...args: any) => void
) {
  const vm = getCurrentInstance()
  const merge = Vue.config.optionMergeStrategies[name]
  if (vm && merge) {
    const prototype = Object.getPrototypeOf(vm.proxy.$options)
    prototype[name] = merge(vm.proxy.$options[name], callback)
  }
}

export function onBeforeRouteUpdate(callback: NavigationGuard<Vue>) {
  return onHook('beforeRouteUpdate', callback)
}

export function onBeforeRouteLeave(callback: NavigationGuard<Vue>) {
  return onHook('beforeRouteLeave', callback)
}

You can then simply use it as a regular "composable" hook:

import { onBeforeRouteUpdate } from '@/composables/router'

export default {
    setup() {
        onBeforeRouteUpdate((to, from, next) => {
            // your logic
            next()
        })
    }
}

from composition-api.

lbssousa avatar lbssousa commented on April 28, 2024 22

Any progress about this kind of support in @vue/composition-api? Maybe supporting some kind of onBeforeRouteUpdate() / onBeforeRouteLeave() hooks inside setup()?

from composition-api.

ggedde avatar ggedde commented on April 28, 2024 3

Well, I found a work around:

beforeRouteLeave(to: any, from: any, next: any) {
	from.matched[0].instances.default.doFunction();
        next();
},

Although, this doesn't really seem like the ideal solution.
If anyone knows a better way then let me know.
PS. You need to make sure that the function is in the return of the setup() function.

from composition-api.

Codermar avatar Codermar commented on April 28, 2024 2

@melenaos that composition function is a workaround for Vue 2. Vue Router Next should support this natively https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards in Vue 3

from composition-api.

bcronje avatar bcronje commented on April 28, 2024 2

@yassipad It appears that the hooks are not cleaned up properly. I'm using beforeRouteLeave, the first time I leave the route the hook fires once, the second time I leave the hooks it fires twice and so on. Any idea how to cleanup the hook when leaving?

from composition-api.

Genoe avatar Genoe commented on April 28, 2024 2

@bcronje I believe I came up with a simple fix to clean up the hooks. When I log out the prototype[name] object I get an ever-increasing array of functions where each function is the hook defined in my component. Clearing out the value if something is already defined for prototype[name] fixes the issue. Hook function is below:

export function onHook(
  name: keyof ComponentOptions<Vue>,
  callback: (...args: any) => void
) {
  const vm = getCurrentInstance()
  const merge = Vue.config.optionMergeStrategies[name]

  if (vm && merge) {
    const prototype = Object.getPrototypeOf(vm.proxy.$options)
    if (prototype[name]) {
      delete prototype[name];
    }
    prototype[name] = merge(vm.proxy.$options[name], callback)
  }
}

from composition-api.

bcronje avatar bcronje commented on April 28, 2024 1
export function onHook(
  name: keyof ComponentOptions<Vue>,
  callback: (...args: any) => void
) {
  const vm = getCurrentInstance()
  const merge = Vue.config.optionMergeStrategies[name]
  if (vm && merge) {
    const prototype = Object.getPrototypeOf(vm.$options)
    prototype[name] = merge(vm.$options[name], callback)
  }
}

I've had to change

const prototype = Object.getPrototypeOf(vm.$options);
prototype[name] = merge(vm.$options[name], callback);

to

const prototype = Object.getPrototypeOf(vm.proxy.$options);
prototype[name] = merge(vm.proxy.$options[name], callback);

from composition-api.

yassilah avatar yassilah commented on April 28, 2024 1

I edited my initial comment, thanks for the update 👍

from composition-api.

posva avatar posva commented on April 28, 2024

I don't think beforeRouteEnter is possible as it's executed before a view component is mounted. Once the setup function is called, the navigation has already been confirmed

from composition-api.

timshannon avatar timshannon commented on April 28, 2024

How are you working around this? Watchers on props?

from composition-api.

lbssousa avatar lbssousa commented on April 28, 2024

@timshannon for the beforeRouteEnter guard, I suppose a possible workaround is using beforeEnter guard (defined directly in router, not in component) instead. For the other guards, one needs to deliver proper hooks like onBeforeRouteUpdate and onBeforeRouteLeave for Composition API.

What I'm doing in the moment is just not using Compostion API at all for components that need navigation guards.

from composition-api.

ggedde avatar ggedde commented on April 28, 2024

The beforeRouteEnter is working for me. Looks like it was added in v0.3.2.

from composition-api.

ggedde avatar ggedde commented on April 28, 2024

A couple of notes that I found when using beforeRouteEnter:
any data that you pass back to the component like

next(vm => {
	vm.foo = 'bar';
});

Won't be available in the setup() method initially. It will work in your template as long as you pass it through the return{}, but if you need to access the data in the setup method you will need to do 2 things.

  1. Access after a tick like context.root.$nextTick(() => { console.log(foo);});
  2. The variable has to be declared in the return{} even if you don't plan on using it in the template. I am not sure why this is the case. This just might be a bug with the plugin and will get resolved with Vue 3, but I will keep track of the issue #184.

from composition-api.

ggedde avatar ggedde commented on April 28, 2024

Navigation guards only seem to work when accessing the vm in the next() function.

However, I can't get them to work otherwise.
example:

export default createComponent({
	beforeRouteLeave() {
		doFunction();
	}
	setup() {
		function doFunction() {
			// Do Stuff
		}
                 return {
                        doFunction
                 };
	}
}

How can I call a function from beforeRouteLeave that is in the setup() function without using the next(vm => {});
In the Options API you would use this.doFunction(); So what is the equivalent in the new Composition API?

from composition-api.

ggedde avatar ggedde commented on April 28, 2024

Well, now I need to use beforeRouteUpdate and running next() seems to re-run setup(). Which in turn overwrites any data I set using "from.matched[0].instances.default". I tried using a setTimeout or NextTick, but the data does not get updated. So it seems that beforeRouteEnter works has you have access to the callback and beforeRouteLeave works sort of with "from.matched[0].instances.default". But beforeRouteUpdate doesn't seem to work as you lose context after next() runs.

The issue I am facing is switching routes within path/:id
so switching from path/12 to path/13 doesn't allow me to swap out the item.

from composition-api.

sseeland avatar sseeland commented on April 28, 2024

Any new on this? I've got a similar problem. I want to call a function from beforeRouteLeave(). The function is defined inside of setup() in order to have access to the refs that are defined there.

So my code looks a bit like this (abbreviated):

setup() {
  const titleEditor = ref(null);
  const textEditor = ref(null);

  function flushEditors() {
    if (textEditor.value) {
      textEditor.value.flushEvents();
    }

    if (titleEditor.value) {
      titleEditor.value.flushEvents();
    }
  }

  return { flushEditors };
},

beforeRouteLeave(_to: Route, _from: Route, next: NavigationGuardNext) {
  this.flushEditors();
  next();
}

This works, but TypeScript is throwing errors because there is no typing for flushEditors() on this. Also, I have a feeling that this won't work in Vue 3. Any ideas or hints?

from composition-api.

melenaos avatar melenaos commented on April 28, 2024

The composable function of @yassipad doesn't work in Vue3 because of Vue.config.optionMergeStrategies.

Is there anyone that could point me to the right direction to make this work in Vue3?

Also the beforeRouteLeave isn't triggered in Vue3. Maybe it's removed as suspected by @sseeland.

from composition-api.

melenaos avatar melenaos commented on April 28, 2024

from composition-api.

seyfer avatar seyfer commented on April 28, 2024

@yassipad hello, thank you for the solution. Unfortunately with vue2 composition api it does not work for me

export const Navbar = defineComponent({
  setup() {
    onBeforeRouteUpdate((to, from, next) => {
      console.log([to, from]);

      next();
    });

nothing happens when I switch route. maybe because we use children routes?
the main route always remains / and only children routes will change on navigation.
what would be a solution in this case?

from composition-api.

seyfer avatar seyfer commented on April 28, 2024

this is what I ended up with

export const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
});

const observableRoute: {route: Route | {}} = {
  route: {}
};
const routeData = Vue.observable(observableRoute);
router.afterEach(route => {
  routeData.route = route;
});

export function useCurrentRoute() {
  return computed(() => routeData.route as Route);
}

and usage

setup() {
    const currentRoute = useCurrentRoute();

from composition-api.

yassilah avatar yassilah commented on April 28, 2024

@seyfer Hi, I just made a very quick test using Vue2+VueComposition API and it seems to be working fine (https://codepen.io/yassipad/pen/gOMjzOe). What do you mean when you say you're using children routes?

@melenaos The hooks are indeed already available in VueRouter Next. Also, Vue.config.optionMergeStrategies is still accessible but it is now context specific: vm.root.appContext.config.optionMergeStrategies.

from composition-api.

yassilah avatar yassilah commented on April 28, 2024

@bcronje I'm assuming you're using Vue 3 (since you're using "proxy"), in which case you shouldn't have to use my helper functions as those are already included in vue-router https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards

from composition-api.

bcronje avatar bcronje commented on April 28, 2024

@yassipad no Vue 2 with latest composition API. vm.$options is undefined for me. getCurrentInstance returns ComponentInternalInstance that does not have $options defined, it is however defined on ComponentInstance which is available under proxy. Unless I'm doing something wrong.

from composition-api.

yassilah avatar yassilah commented on April 28, 2024

@bcronje Sorry, you're absolutely right, I didn't see that was changed since:
https://github.com/vuejs/composition-api/releases/tag/v1.0.0-beta.22

from composition-api.

bcronje avatar bcronje commented on April 28, 2024

I edited my initial comment, thanks for the update 👍

Thank you for the code, saved me quite some time to figure it out by myself!

from composition-api.

bcronje avatar bcronje commented on April 28, 2024

@bcronje I believe I came up with a simple fix to clean up the hooks. When I log out the prototype[name] object I get an ever-increasing array of functions where each function is the hook defined in my component. Clearing out the value if something is already defined for prototype[name] fixes the issue. Hook function is below:

export function onHook(
  name: keyof ComponentOptions<Vue>,
  callback: (...args: any) => void
) {
  const vm = getCurrentInstance()
  const merge = Vue.config.optionMergeStrategies[name]

  if (vm && merge) {
    const prototype = Object.getPrototypeOf(vm.proxy.$options)
    if (prototype[name]) {
      delete prototype[name];
    }
    prototype[name] = merge(vm.proxy.$options[name], callback)
  }
}

@Genoe I ended doing something very similar, basically return an unregister hook that does the same delete:

export function onHook(name: keyof ComponentOptions<Vue>, callback: (...args: any) => void): () => void {
  const vm = getCurrentInstance();
  const merge = Vue.config.optionMergeStrategies[name];
  if (vm && merge) {
    const prototype = Object.getPrototypeOf(vm.proxy.$options);
    prototype[name] = merge(vm.proxy.$options[name], callback);
    return () => {
      delete prototype[name];
    };
  }
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  return () => {};
}

Then using the hooks I can unregister before leaving e.g.

const unregisterRouteLeave = onBeforeRouteLeave(async (_to, _from, next) => {
    if (await confirmUnsavedForm(originalModel.value, model.value)) {
      unregisterRouteLeave();
      next();
    } else {
      next(false);
    }
});

from composition-api.

github-actions avatar github-actions commented on April 28, 2024

Stale issue message

from composition-api.

seyfer avatar seyfer commented on April 28, 2024

@seyfer Hi, I just made a very quick test using Vue2+VueComposition API and it seems to be working fine (https://codepen.io/yassipad/pen/gOMjzOe). What do you mean when you say you're using children routes?

@yassilah When you have index.ts, and then another index with routes for each section, like f.e. todo-index.ts and post-index.ts
Then we can define index routes

import todoRoutes from `todo-index.ts`; 
import postRoutes from `post-index.ts`;

const routes: RouteConfigSingleView[] = [
  {
    path: '/todos',
    name: 'todos',
    component: TodosLayout,
    children: todoRoutes,
  },
  {
    path: '/'posts,
    component: PostsLayout,
    children: postsRoutes,
  },

And in the children routes we have

export const todoRoutes: RouteConfigSingleView[] = [
  {
    path: '/add',
    name: 'add-todo',
    component: AddTodoPage,
  },
export const postRoutes: RouteConfigSingleView[] = [
  {
    path: '/edit',
    name: 'edit-post',
    component: EditPostPage,
  },

this is an example of when it does not work.

from composition-api.

daxiongz avatar daxiongz commented on April 28, 2024
export default createComponent({
	beforeRouteLeave() {
		doFunction();
	}
	setup() {
	}
}

"@vue/composition-api": "1.0.5"
Like this, I write it in page view. It run normally!

from composition-api.

francyleo avatar francyleo commented on April 28, 2024
export function onHook(
  name: keyof ComponentOptions<Vue>,
  callback: (...args: any) => void
) {
  const vm = getCurrentInstance()
  const merge = Vue.config.optionMergeStrategies[name]
  if (vm && merge) {
    const prototype = Object.getPrototypeOf(vm.$options)
    prototype[name] = merge(vm.$options[name], callback)
  }
}

I've had to change

const prototype = Object.getPrototypeOf(vm.$options);
prototype[name] = merge(vm.$options[name], callback);

to

const prototype = Object.getPrototypeOf(vm.proxy.$options);
prototype[name] = merge(vm.proxy.$options[name], callback);

It worked for me in Vue 2.7 and Vue Router ^3.4.9

from composition-api.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.