As some of you may or may not know the Vue-router 2.3.0 does not handle arrays in GET Query parameters to spec. Let me explain.
When you want to send an array of items to the server, from an array of checkboxes for example, you do some-array[]
which translates to some-array[]=2&some-array[]=3
in the URL. Then on the server side of this, in PHP for example, you can do $_POST['some-array']
and this will return an array with [1,2]
. Well Vue-router handles things a bit differently.
Prior to 2.0 it used to handle things to spec, now when you pass an array of string items Vue-router just transforms them one by one like so some-array=1&some-array=2
which is bad for a few reasons.
- This is not up to spec and handling on the server side is not possible. When trying to access this with PHP you just receive the last
some-array
item in the URL, which will be2
in our case. - When there is only one of these parameters:
some-array=1
Vue-router will return1
as a string, because it does not know it should be an array. If there are more than one params, it will return an array of items.
Also if you try to pass an object this get even worse, in the URL we get [Object object].
I along with some other users noted this issue on Github and a potential fix is on its way, but until then, this is what I thought of.
If you just need to handle this on the client, you can make an accessor as a computed property that always returns an array for that parameter:
{ computed: { someArray(){ const someArray = this.$route.query.someArray return Array.isArray(someArray) ? someArray : [someArray] } } }
OK so that’s fine and dandy, we now get an array of someArray every time, even if its only one element but accessing the computed property. What about when we refresh our page? What if we want to access this param from the server? Well we cant. As I noted above, we get only the last defined item in the URL. The simplest workaround I managed to think of is using a comma separated list of values.
// Use this because Array.split() on empty string returns [''] which is not what we need atm. function split (string, sep) { const a = string.split(sep) if (a[0] === '' && a.length === 1) return [] return a } export default { data () { return { query: { someArray: 'first, second, third' // gets transformed into ['first', 'second', 'third'] from the computed prop } } }, computed: { someArray: { get () { /** * our split() function returns a new array from the comma separated list. */ return split(this.query.someArray, ',') }, set (value) { /** * Array.toString() returns a comma separated list of array items. */ this.query.someArray= value.toString() } } } }
Now we can even use a v-model in our templates, it will use the computed property`s get to get an array, and its set to transform the array into a comma separated list.
<template> <div> <h2>Check some values</h2> <label v-for="array in someArray"> <input type="text" name="someArray" :value="array" v-model="someArray"> {{array}} </label> {{ someArray }} </div> </template>
In the backend you could transform that comma separated string into an array again, with PHP you can do explode(',', $_POST['some-array'])
.
Here is a webpackbin to demonstrate how it works:
If you liked this post and want to read more, please feel free to subscribe to our newsletter. We will send you one, maximum two emails a month, just to recap what topics have come out, pinky swear 😉
> This is not up to spec and handling on the server side is not possible.
Which specification? RFC 3986 (The URI specification: https://tools.ietf.org/html/rfc3986) makes no restrictions on how to pass arguments through a query string. The URI template RFC (https://tools.ietf.org/html/rfc6570#section-3.2.8) produces query strings precisely the way Vue Router does, when using the {?args*} template.
Are you suggesting Vue Router’s serializers should be coupled with PHP? Because that is a bad idea.
Hehe if you read the post, you would see what I mean. It did not handle arrays the same way HTML forms do and how PHP handles them, thus if you have a mixed PHP rendered app with Vue SPA hydration layer, you would have a hard time parsing those query params back and forth.
Thank you for taking your time to go through those specs 🙂
I did read the post and do know what you mean, I’ve run into this exact issue. And I agree with you about the ambiguity of a single query argument (URI templates have the same problem).
But PHP’s inability to parse `?foo=bar&foo=baz` is not Vue Router’s fault, because that format does not violate any specifications. This is a limitation of PHP, not Vue Router.
And I don’t agree that this doesn’t match the behaviour of HTML forms, either. Regular `method=”get”` query parameters are encoded using the `x-www-form-urlencoded` media type, and its specification says nothing about adding square brackets to indicate multiple entries per key: https://url.spec.whatwg.org/#application/x-www-form-urlencoded.