-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Component guards not working when defined on mixins #454
Comments
It wasn't intentionally removed but I will have to check a way to apply the mixins from components and its |
the same for beforeRouteUpdate and beforeRouteLeave events |
On the same topic has anything changed with |
Are there any possibilities or some workaround for solving this issue? |
Router hooks like beforeRouteLeave, beforeRouteUpdate, beforeRouteEnter do not resolve in this. $options.
If you do this in this.$options everything resolves, but still doesn't work (only the method from the component is executed, not from the mixin) |
Bummer! Ran into this issue today as well. My use-case is to have my in-component guards placed into a page mixin for easy reuse across multiple similar page components. This worked just fine in vue router 3. Either this is simply a regression or such a use case is no longer supported? Seems odd that it would be the latter case when mixins are still a supported feature in vue 3? |
Also experiencing the same issue. For example using in ...
data: () => ({}),
beforeRouteEnter(from, to, next) {
console.log('beforeRouteEnter') // not getting triggered
next()
},
computed: {
... |
@dpmango That's not supposed to work. Route guards only work when used in route components, not just any components. |
Will the problem be fixed? |
You can use my solution mergeRouterGuard.js export const NeedMergeGuards = ['beforeRouteEnter', 'beforeRouteUpdate', 'beforeRouteLeave']
export function mergeFuncUtil (nextHook, prevHook) {
return function (to, from, next) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const ctx = this
function reduceNext () {
return prevHook.bind(ctx)(to, from, next)
}
return nextHook.bind(ctx)(to, from, reduceNext)
}
}
/** execution order: the guard in component is executed last one
* @param distOption
* {
name: 'test',
mixins: [myMixin],
beforeRouteEnter (to, from, next) { next(this => {}) },
beforeRouteUpdate (to: any, from: any, next: Function) { next() },
beforeRouteLeave (to, from, next) { next() }}
}
* @param customMixins
[
{
beforeRouteEnter: Function
},
{
beforeRouteEnter: Function
}
]
* @param needMergeHooks ['beforeRouteEnter', 'beforeRouteUpdate', 'beforeRouteLeave']
* @returns
* {
name: 'test',
mixins: [myMixin],
beforeRouteEnter (to, from, next) { allNext(this => {}) },
beforeRouteUpdate (to, from, next) { allNext() },
beforeRouteLeave (to, from, next) { allNext() }
* }
*/
export const mergeRouterGuard = (
distOption,
customMixins,
needMergeGuards = NeedMergeGuards
) => {
needMergeGuards.forEach((mergeGuard) => {
const customMergeGuards = customMixins
.filter(customMixin => customMixin[mergeGuard])
.map(customMixin => customMixin[mergeGuard])
if (customMergeGuards.length > 0) {
const customFunc = customMergeGuards.reduce(function (mergeFunc, customMergeGuard) {
const fn = mergeFuncUtil(mergeFunc, customMergeGuard)
return fn
})
const finalHook = !distOption[mergeGuard] ? customFunc
: mergeFuncUtil(customFunc, distOption[mergeGuard])
distOption[mergeGuard] = function (...args) {
return finalHook.bind(this)(...args)
}
}
})
return distOption
} step two: step three: use in component, give an example Test.vue <script lang="ts">
import { defineComponent } from 'vue'
import { myMixin } from './myMixin'
import { myMixin2 } from './myMixin2'
import { mergeRouterGuard } from '../mergeRouterGuard'
const mixins = [ myMixin, myMixin2 ]
// vue option
const option = {
name: 'test',
data () {
return {
msg: 'test'
}
},
mixins:, // important !!!!!!!, mergeRouteGuard only merge router guard
beforeRouteEnter (to, from, next) {
console.log('beforeRouteEnter')
next((...args) => console.log(args))
},
beforeRouteUpdate (to, from, next) {
console.log('---test.vue---------from test.vue-----test.vue-----')
console.log(this)
next((...args) => console.log(args))
},
beforeRouteLeave (to, from, next) {
console.log('beforeRouteLeave')
next((...args) => console.log(args))
}
}
// merge router guard
const mergeRouterGuardOption = mergeRouterGuard(option, mixins)
export default defineComponent(mergeRouterGuardOption)
</script> if you has bug, can connect me |
This comment has been minimized.
This comment has been minimized.
Hey everybody, I found a solution. I hope it helps everybody out. In your mixin, you can create a function called const mixin = {
created(){
let route = this.$route.matched[0];
if(!route)
return;
route.leaveGuards = new Set([function(to, from, next){
console.log("to", to, "from", from, "next", next);
next();
}]);
}
} |
vue-router does not support navigation guards in mixins: <vuejs/router#454> For now remove the mixin and duplicate the code in every component (keep this branch rebaseable). In the future, this is a candidate for using the composition api: <https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards>
Now that Vue3 is now the default, would someone from the vue-router core team be willing to speak on this issue? This seems to be the only breaking change mentioned in the migration guide with no reasonable upgrade path. The migration guide even links to this open issue to "track its support". It seems some people on this thread have found reasonable workarounds, but will support ever added to vue-router core? Also, its okay to say "no we wont support it". That's a fine response, but it would be great to get clarity and update the migration guide to mention this. |
vue-router does not support navigation guards in mixins: <vuejs/router#454> For now remove the mixin and duplicate the code in every component (keep this branch rebaseable). In the future, this is a candidate for using the composition api: <https://next.router.vuejs.org/guide/advanced/composition-api.html#navigation-guards>
Faced same issue, i use vue-property-decorator and firstly thought that issue lies in it.
|
If the goal of using mixins is to extract the logic of your navigation guards, maybe this library can serve as a workaround : https://github.com/abellion/vue-router-shield (disclaimer : I'm the creator of this library) |
The workaround we found was to import the mixin with a spread operator instead. This method requires only a single line change in the components that use the mixin. navigationGuardsMixin.js
YourComponent.vue
|
So it's still not fixed? I'm migrating my app and I used following workaround for now: mixins: [formPageMixin],
beforeRouteLeave (...args) {
return formPageMixin.beforeRouteLeave.apply(this, ...args)
}, |
a couple of hours trying to find why's not working since migrating the all projet from vue2 to vue3. Yeah it's in the migration guide but flooded in the content... Yet, mixins are an extremly usefull feature. Thanks @leboeuf for the workaround |
it should be |
You can also do it this way -- just define the nav guard as an object containing a method where both are named for the nav guard like so:
|
Hi, first of all, thank you for this great library! :) |
I think you need to use: const route = this.$route.matched.slice(-1)[0] |
Hi,
The v3.x documentation about navigation guards doesn't say much about multiple callback function calls, but to make it short: Example 1: export default {
mixins: [
// Mixin 1, called first
{
beforeRouteEnter(to, from, next) {
next();
}
},
// Mixin 2, called second
{
beforeRouteEnter(to, from, next) {
next(
// Call delayed after mixin 4
function callback1(vm) {}
);
}
},
// Mixin 3, called in third
{
beforeRouteEnter(to, from, next) {
next(
// Called delayed after mixin 4 and after function callback1
function callback2(vm) {}
);
}
},
// Mixin 4, called in fourth
{
beforeRouteEnter(to, from, next) {
next();
}
}
]
}; Example 2: export default {
mixins: [
// Mixin 1, called first
{
beforeRouteEnter(to, from, next) {
next();
}
},
// Mixin 2, called second
{
beforeRouteEnter(to, from, next) {
next(
// Never called, because mixin 4 didn't confirm the navigation
function callback1(vm) {}
);
}
},
// Mixin 3, called in third
{
beforeRouteEnter(to, from, next) {
next(
// Never called, because mixin 4 didn't confirm the navigation
function callback2(vm) {}
);
}
},
// Mixin 4, called in fourth
{
beforeRouteEnter(to, from, next) {
next(false);
}
}
]
}; Here is my solution: mergeMixinGuards.js: // Taken from https://github.com/vuejs/router/blob/main/packages/router/src/types/typeGuards.ts
function isRouteLocation(route) {
return typeof route === 'string' || (route && typeof route === 'object');
}
function mergeGuards(to, from, next, context, guards, callbacks = []) {
guards.shift().call(context, to, from, nextArg => {
// See guardToPromiseFn() in https://github.com/vuejs/router/blob/main/packages/router/src/navigationGuards.ts
if (nextArg === false || nextArg instanceof Error || isRouteLocation(nextArg)) {
next(nextArg);
return;
}
if (typeof nextArg === 'function') {
callbacks.push(nextArg);
}
if (guards.length > 0) {
mergeGuards(to, from, next, context, guards, callbacks);
return;
} else if (callbacks.length > 0) {
next(vm => callbacks.forEach(callback => callback(vm)));
return;
}
next();
});
}
export function mergeMixinGuards(optionsApi) {
if (!optionsApi?.mixins || optionsApi.mixins.length == 0) {
return optionsApi;
}
const allApis = [...optionsApi.mixins, optionsApi];
for (const guardName of ['beforeRouteEnter', 'beforeRouteUpdate', 'beforeRouteLeave']) {
const guards = [];
for (const api of allApis) {
if (api[guardName]) {
guards.push(api[guardName]);
}
}
if (guards.length == 0) {
continue;
}
optionsApi[guardName] =
guards.length == 1
? guards[0]
: function (to, from, next) {
mergeGuards(to, from, next, this, [...guards]);
};
}
return optionsApi;
} And simply wrap your component with import { mergeMixinGuards } from './mergeMixinGuards';
export default mergeMixinGuards({
name: 'MyComponent',
mixins: [...],
beforeRouteEnter(to, from, next) {
next(vm => {
// Callback function (mixins can have some too)
});
},
...
}); |
Version
4.0.0-beta.9
Reproduction link
https://jsfiddle.net/ukeagtjm/
Steps to reproduce
...
What is expected?
beforeRouteEnter() to run and log from mixin
What is actually happening?
beforeRouteEnter() is never executed
The text was updated successfully, but these errors were encountered: