vuejs / router

🚦 The official router for Vue.js
https://router.vuejs.org/
MIT License
3.88k stars 1.18k forks source link

Optional path segments #2181

Closed mrkamel closed 6 months ago

mrkamel commented 6 months ago

What problem is this solving

We are looking for a way to specify optional path segments in a route, like e.g.

/companies/:companyNameSlug(/page/:page)?

meaning, the /page/:page part is optional. Either the whole segment is present when the page param is present or fully not present. However, this does not seem to be possible and all my research ended without a solution. path-to-regexp (not sure if vue-router is still using it) actually supports this via curly braces

/companies/:companyNameSlug{/page/:page}?

but it seems vue-router is escaping this before handing it over when i debug it using the path ranker tool https://paths.esm.dev/

I found some example https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js#L24 which looks like it should be possible but this is either outdated or not correctly supported by by the path ranker tool, as the braces also get escaped and it also does not work in my app.

Thanks in advance

Proposed solution

Optimally, the curly braces would be supported to implement optional path segments

/companies/:companyNameSlug{/page/:page}?

Describe alternatives you've considered

No response

posva commented 6 months ago

See https://router.vuejs.org/guide/migration/#Removal-of-unnamed-parameters

In particular, what you want is this: https://paths.esm.dev/?p=AAMsIPQgYAwL9gdgGALADgWWMzAFpBLHCWgc4PKyMmA2gLgCbw4OigVg_AC5eGykoA1SD10A1AAAHATokaYUwAAQ&t=/companies/2/page/thing#

mrkamel commented 6 months ago

@posva thanks a lot :+1: The neccessary escaping of the closing brace is very weird, though, IMHO

mrkamel commented 6 months ago

Additionally, this doesn't fully help with generating a url as the (?:page/([^/]+?\))? is only a constraint when parsing a path, but has no effect when generating one

const matcher = createRouterMatcher(
  [{ name: "someRoute", path: "/prefix/:slug/:page(?:page/(\\d+\\))?" }],
  {},
);

matcher.resolve(
  { name: "someRoute", params: { slug: "test-123", page: 4 } },
  { params: {} },
)

// => /prefix/test-123/4

reproduction https://codesandbox.io/p/devbox/qpktzz?file=%2Findex.mjs%3A11%2C1-15%2C1

posva commented 6 months ago

Escaping always reads weird. Yes, you will have to provide the whole param: page/54. In your specific case though, the / will be encoded since it's a param so you will need to build the fullpath yourself: router.push(/prefix/${slug}/page/${page}). There are plans to improve the handling of the / encoding in params in the future though