vuejs / vue-router

🚦 The official router for Vue 2
http://v3.router.vuejs.org/
MIT License
18.99k stars 5.06k forks source link

Programmatic navigation does not work with "zero or more" route matching #1175

Open nicbou opened 7 years ago

nicbou commented 7 years ago

Vue.js / vue-router versions

2.1.1

Steps to reproduce

What is Expected?

The parsed itineraryItems route param is an array containing 'origin' and 'destination'.

What is actually happening?

The parsed itineraryItems route param is a string with the value 'origin/destination' if the URL is accessed directly.

The parsed itineraryItems route param is an array containing 'origin' and 'destination' if accessed programmatically.

This means the same URL can have its params as an array or as a string, depending on how it was created.

nicbou commented 7 years ago

Additional information:

If I try to use a string in router.push, it gets escaped.

I also found a related ticket: https://github.com/pillarjs/path-to-regexp/issues/80

LinusBorg commented 7 years ago

Hi @nicbou

Thanks for the report. Could you provide an interactive reproduction via jsfiddle or a quick repository? That would help us out.

nicbou commented 7 years ago

Yes of course! Here is a demo: http://jsfiddle.net/ub9e33fm/1/

As you can see, you can only update the path with an array due to escaping, but it is always initially loaded as a string.

nicbou commented 7 years ago

Would you mind changing the ticket label? The repro is right above :)

posva commented 7 years ago

I'm checking the repro, thanks for the reminder

posva commented 7 years ago

@nicbou I checked the repro but it looks good to me:

What is wrong?

nicbou commented 7 years ago

The biggest issue for me is that the page gives me an a string as the param when the page loads, but if I navigate to that same URL programmatically, I get an array. The same URL and route resolve to different parameters. There is no way to navigate to that route and get a string as the params (as it gets escaped).

This is a bug because these should behave consistently and they don't. I would naturally expect an array since there are multiple values for the same parameters, but a string would be trivial to parse too. It just cannot be both at the same time.

In our application, we currently need to do a if Array.isArray(params) to prevent the application from breaking when the param is a string. Although this workaround works, it will need to be applied in multiple places as the application grows.

nicbou commented 7 years ago

Additional details: the value can also be undefined if you navigate to a link without passing the params (which are optional!)

Here is the updated JSFiddle: http://jsfiddle.net/ub9e33fm/3/

Here's the full workaround I need if I want to deal with an array as expected:


      let urlItineraryItems = to.params.itineraryItems;
      if (!Array.isArray(urlItineraryItems)) {
        // https://github.com/vuejs/vue-router/issues/1175
        urlItineraryItems = (urlItineraryItems || '').split('/')
      }
posva commented 7 years ago

I revisited the example and it's true that if we change the path to path: '/:foo*' (with the (.*) , things are working as expected because the / is no longer a delimiter), a link to /foo/baz should yield a param foo: ['foo', 'baz'].

nicbou commented 7 years ago

I would still consider it a bug, as there is no way to obtain a consistent behaviour (params only as a string, or only as an array) with these "zero or more" matchers. I tried the same JSFiddle with the link updated, and I cannot get an array consistently.

In any case, the workaround I have posted works consistently well. If we end up committing to Vue (not my decision), I'll have a look. I suspect it's a bug with path-to-regexp that just carried over to vue-router.

vkruoso commented 7 years ago

I'm also facing this issue. The array trick works but the inconsistency is annoying to handle. I was expecting the zero or more match would always return an array, and that I would always use an array when pushing new routes programatically.