vue-router
vue-router copied to clipboard
Optional parameters are dropped in named nested routes
Version
3.0.1
Reproduction link
~~https://codepen.io/wrrrn/pen/GxqmOb~~ http://jsfiddle.net/7a31maev/1/
Steps to reproduce
I've linked to what the current router code is. The issue is simple, so I hope I'm missing something obvious.
What is expected?
When the :paramOptional is set, and child route is pushed to, the path will look as such - '/search/paramOne/paramOptional/paramTwo/show-modal'
What is actually happening?
When navigated to, the child removes the paramOptional. The resulting url is - '/search/paramOne/paramTwo/show-modal'
Adding a this.$router.push({name:'ShowModal', params: {paramOptional: this. paramOptionalData}})
fixes the issue but makes me sad.
Edited to add an actual repro.
Thanks for writing this one up. I was just about ready to do it myself but found yours :)
I'm wanting to add an optional parameter at the start of my app (/admin?
) to control some admin functionality. For now I'm just using your workaround on adding this in the params.
Let me know if I can help at all
you can work on a fix for this :)
I've created a PR for this :) https://github.com/vuejs/vue-router/pull/2538
@ElkeCodes Does this PR also work for this use case?
/search/:a/(test/:b)?/:c
http://jsfiddle.net/wf517tdu/1/
Does this explain why in the following definition the meterId
parameter is dropped when typing the route in the browser?
{
name: "overview/meters",
path: "/overview/:id/meters/:type/:meterId?",
component: ListsPage,
meta: {
requiresAuth: true,
breadcrumbs: true,
access: ["site", "group"],
},
props: (route) => ({
viewSource: "meters",
options: {
energyType: route.params.type,
expandedMeterKeys: route.params.meterId ? [route.params.meterId] : [],
},
}),
},
So what's the status on this one? It is quite annoying to have to define two (almost identical) routes just to be able to use optional parameters.
If someone is still looking for a better solution than having to define the optional param in every route object, I have found a solution although it is pretty hacky...
So I had the need to define an optional :lang
parameter for my paths like this: path: '/:lang(en|bg|de)?/whatever/page'
and this bug made the redirects go to /whatever/page
even if the current route had the optional ':lang' part in its url.
So the first thing I had to do is force all route navigations to include the ':lang' as a param of the navigation if it was part of the current route:
// Global hook for fixing navigation with optional params removing the params from the path in some cases: https://github.com/vuejs/vue-router/issues/2110
// Needed for supporting our optional :lang parameter in routes
router.beforeEach((to, from, next) => {
// Append the :lang param to the 'to' route if it exists on the 'from' route.
if (from.params && from.params.lang) {
if (to.params)
to.params.lang = from.params.lang;
else
to.params = { lang: from.params.lang };
}
// Force navigation with a defined :lang param if the currently resolved path doesn't include it
if (to.params && to.params.lang
&& to.fullPath != `/${to.params.lang}`
&& !to.fullPath.startsWith(`/${to.params.lang}/`)) {
next({
name: to.name,
hash: to.hash,
params: to.params,
query: to.query,
});
} else {
next();
}
});
So far so good, however after that, for all <router-link>
elements the active-class
stopped working. Which happened because the route defined in the <router-link>
elements didn't have the optional :lang
param and the route was resolved without the :lang
part and thus technically it was a different route.
So I had to override the VueRouter 'resolve' method (which is used internally by <router-link>
to check if the route is active) like so:
// Create Custom Router, to circumvent a vue-router bug with optional path parameters: https://github.com/vuejs/vue-router/issues/2110
// Needed for supporting our optional :lang parameter in routes
class CustomVueRouter extends VueRouter {
// Override resolve method. Fixes router-link active-class not working when the :lang param is not explicitly defined.
resolve(to, current, append) {
// Append the :lang param to the 'to' route if it exists on the current.
if (current && current.params && current.params.lang) {
if (to.params)
to.params.lang = current.params.lang;
else
to.params = { lang: current.params.lang };
}
return super.resolve(to, current, append);
}
// Override replace method. Fixes replace usage when the :lang param is not provided, but the current route has it defined.
replace(location) {
// Append the :lang param to the 'location' for replacing if it exists on the current route.
if (this.history && this.history.current && this.history.current.params && this.history.current.params.lang) {
if (location.params)
location.params.lang = this.history.current.params.lang;
else
location.params = { lang: this.history.current.params.lang };
}
return super.replace(location);
}
}
Vue.use(CustomVueRouter);
const router = new CustomVueRouter({
routes,
mode: 'history'
});
It again adds the :lang
param to the route for resolution if it is present on the current route.
Also as seen in the code snippet, I had to override the replace
method, because it wasn't working correctly when :lang
param was not provided and the current route had it defined.
Hopefully this helps someone fix their usage of optional params to behave more appropriately :)