Skip to content
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

Failed to pass Object data through named routes when migrating from vue-router 3.1.5 #494

Closed
jackjtr opened this issue Sep 25, 2020 · 14 comments

Comments

@jackjtr
Copy link

jackjtr commented Sep 25, 2020

Version

4.0.0-beta.12

Reproduction link

https://codesandbox.io/s/affectionate-sunset-dng3r?fontsize=14&hidenavigation=1&theme=dark

Steps to reproduce

this.$router.push({
  name: `Target-Page`,
  params: {
    data: {
      fieldA: 'a',
      ...
      filedZ: false,
    }
})

What is expected?

this.$route.params?.data is supposed to be an Object data

What is actually happening?

this.$route.params?.data becomes a String data

data: "[object Object]"


Our projects are heavily using route.params to pass dynamic data from page to page.
We also tried "Passing Props to Route Components", which still cannot meet our demand.
We have to use JSON.stringify/JSON.parse/Number()/Boolean() everywhere to pass or retrieve dynamic data if this behaviour is correct for vue-router 4.x.

Looking forward to see a proper explanation.

Thanks!

@posva
Copy link
Member

posva commented Sep 25, 2020

Passing data in params has never been supported. It will even break in some scenarios in vue router 3. Params must be present on the url.

Alternatives are passing data in a store, query params, or using the history state.

@posva posva closed this as completed Sep 25, 2020
@jackjtr
Copy link
Author

jackjtr commented Sep 27, 2020

@posva I am sorry that I have to tell the truth, which is vue-router 3.x do support passing object/boolean/number/etc data in params.

here is the reproduction link
https://codesandbox.io/s/long-lake-zce24?fontsize=14&hidenavigation=1&theme=dark

I have no idea what makes you feel like "Passing data in params has never been supported", but it really exists.

It is grateful if anyone can tell why passing Object data in named routes, which is supported in 3.x, now fails in vue-router 4.x beta.
Thank you!

@Splinterjke
Copy link

@jackjtr you get the encoded object being a string representing its type because the encodeParam is applied to your data object. Currently you can't mount components with raw data object being passed through router props. The simplest workaround is to stringify your object, pass as router param and parse back in target component. I do agree that looks dirty, but still.

@posva fortunately, passing data in params has been supported for ages (until vue 3 and vue-router 4 are there)
https://jsfiddle.net/5fgmsLcd/

@Splinterjke
Copy link

Splinterjke commented Nov 29, 2020

@jackjtr In terms of router navigation to the component that's supposed to handle your data from previous page, It's trully better to pass common typed object like id as prop and then use it to retrieve the certain object from the collection in your store (whether it's been already populated with the data you need), or even better to get rid of that id props and use /:id router param to request a full object model from your api.

@Liwoj
Copy link

Liwoj commented Apr 20, 2021

@Splinterjke I think what @posva means by "not supported" is that router was never written in a way this works in all scenarios. Yes, you could pass an objects when calling $router.push and it worked but as soon as user used browser navigation (back, forward) or copy/pasting URL's, such solution was ultimately broken...

I personally answered few questions on SO where users was perplexed by exactly this. So IMHO it is a good thing this is completely shut down in the new router...

@Splinterjke
Copy link

@Liwoj totally agreed, a passing some complex data through a router is not the best practice and it should be avoided since there is a lot of more proper ways to implement a state management. There is a good one called pinia by the way.

@jackjtr
Copy link
Author

jackjtr commented Jun 9, 2021

@Splinterjke @Liwoj Thanks for commenting. Actually we are using $router.push this way on Hybrid App, which won't allow user jump to any specific page but only the entrance. Most of the pages are supposed to be controlled through configurations on the server side. We tried using JSON.stringify and JSON.parse with Composition API and it works fine now.

@johnRivs
Copy link

Here's hoping they revert to v3 behavior for a small convenience (to some of us) in the future so we can get rid of JSON.stringify/JSON.parse in our projects.

@mirko77
Copy link

mirko77 commented Oct 5, 2021

+1 for hybrid apps. We are rewriting an Ionic app from Angular JS to Vue 3 and this behaviour is quite a bummer

@vincerubinetti
Copy link

vincerubinetti commented Mar 23, 2022

I don't see why the RouteLocation can't be made to have an arbitrary data field that gets passed along wherever it goes. Not sure how that could break things. You already have a meta field which accepts arbitrary data, but it doesn't support being set from push.

And clearly this is a heavily needed feature.

Alternatives are passing data in a store, query params, or using the history state.

At the very least, this should be somewhere in the docs, with examples.


Per this comment:

We're against passing the state invisibly in the url

Even though there are many valid use cases brought up in that thread.


Edit: looks like there is now an RFC to solve this: vuejs/rfcs#400

I hope this gets implemented very soon.

@AlexDaniel
Copy link

Stumbled upon this when migrating from Vue 2 (where it used to work without any issues) to Vue 3, my two cents: It was a little difficult to debug because I couldn't quite figure out why would an object silently turn into a string… But I found this ticket which made it all clear. I only have a couple of uses, so I guess I can find a way to do it differently, but as people migrate to Vue 3 there will be more people looking for a way to make it work. To me it always seemed like a reasonable solution for passing the data when navigating around.

@zenflow
Copy link

zenflow commented Jun 22, 2022

I agree with @Liwoj that this is a good change going forward. @AlexDaniel If it seemed like a reasonable solution, then maybe you didn't consider what happens when page is refreshed or link is saved/shared, or maybe it just wasn't of importance.

That said, I agree with @AlexDaniel that "as people migrate to Vue 3 there will be more people looking for a way to make it work".. for example, our (very large, complicated, difficult-to-test) app depends on the vue-router 3 (Vue 2) behavior and it would be a huge effort to refactor it to manually serialize/deserialize the objects (and numbers, booleans, etc).

@posva At the very least this should be noted as a breaking change. And ideally (in perfect world) there would be some kind of migration path that isn't so potentially complicated...

@zenflow
Copy link

zenflow commented Jun 22, 2022

I discovered the following workaround to restore vue-router 3 (Vue 2) behavior:

import { createRouter, createWebHistory } from 'vue-router'
+import { stringifyQuery } from 'vue-router'
export const router = createRouter({
  history: createWebHistory(),
+ stringifyQuery: (query) => stringifyQuery(query),
  // ...
})

This works thanks to the implemented behavior of skipping query object normalization when stringifyQuery option is provided: #328 (comment) ea65066

@posva what do you think about adding a skipQueryNormalization option to make this configuration explicit, and an entry in the migration guide? I would be happy to contribute. I suppose this could be used to force skip or force not skip, i.e. documentation would state "the default is true if stringifyQuery is provided and otherwise false"

@AlexDaniel
Copy link

I agree with @Liwoj that this is a good change going forward. @AlexDaniel If it seemed like a reasonable solution, then maybe you didn't consider what happens when page is refreshed or link is saved/shared, or maybe it just wasn't of importance.

Just to clarify and give you an example, I was using it for simple breadcrumbs-like history functionality. The extra data in the router link is needed to mark the route change as “the user is clicking on the breadcrumbs themselves, do not add new entries”. Then the logic that manages the history/breadcrumbs can take that info into account and discard some entries that we don't need. Page refreshes, saving links, etc. is completely irrelevant here, and the use case is valid, because it's about how the navigation happens and not where.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants