qs: Option to not collapse arrays with missing indices.
The following test:
$ node -pe 'require("qs").parse("option[1]=1&text[0]=&text[1]=&text[2]=")'
Produces:
{ option: [ '1' ], text: [ '', '', '' ] }
The option array is collapsed and the missing index 0 is removed. According to the qs documentation, this behavior is intentional. However, it causes problems when handling arrays of checkboxes on a form with e.g. body-parser.
Consider:
index.js:
var express = require("express");
var site = express();
site.use(require("body-parser").urlencoded({extended:true}));
site.get("/", function (req, res) {
res.sendFile(__dirname + "/test.html");
});
site.post("/form", function (req, res) {
res.json(req.body);
});
site.listen(8081);
test.html:
<html>
<body>
<form method="post" action="/form">
<input type="checkbox" name="option[0]" value="1">
<input type="checkbox" name="option[1]" value="1">
<input type="checkbox" name="option[2]" value="1"><br>
<input type="text" name="text[0]"><br>
<input type="text" name="text[1]"><br>
<input type="text" name="text[2]"><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
In that example, if I were to check only option[1], I verify that the index is set correctly by inspecting the request in Chrome, body contents are:
option[1]:1
text[0]:
text[1]:
text[2]:
Yet qs collapses the option array and body-parser ultimately produces the following in req.body:
{"option":["1"],"text":["","",""]}
Similarly, if I were to check option[0] and option[2], the request body would have:
option[0]:1
option[2]:1
text[0]:
text[1]:
text[2]:
But it would be parsed to:
{"option":["1","1"],"text":["","",""]}
All information about which checkbox was checked is lost.
The only way, then, to parse checkbox arrays on forms is workarounds like the clever but hacky one at http://stackoverflow.com/a/41093135, which basically involves hidden inputs with the same name as the checkboxes, then using Array.isArray() to determine if a result is true or false.
I had originally opened this issue at https://github.com/expressjs/body-parser/issues/211 before I knew it was qs.
To that end, I’d like an option to not collapse arrays, such that the previous examples yield things like:
{"option":[null,"1"]}
Or:
{"option":["1",null,"1"]}
This would make parsing arrays of checkboxes on forms possible, which it currently is not without trickery.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 1
- Comments: 16 (9 by maintainers)
Commits related to this issue
- Issue #181 Added option for collapsing arrays with missing indices — committed to 1cheese/qs by deleted user 5 years ago
- Issue #181 Renamed option for collapsing arrays with missing indicies — committed to 1cheese/qs by deleted user 5 years ago
- Issue #181 Fixed error in checking allowSparse option — committed to 1cheese/qs by deleted user 5 years ago
- Issue #181 Added test for allowSparse option — committed to 1cheese/qs by deleted user 5 years ago
- Issue #181 Disabled no-sparse-arrays eslint rule for tests — committed to 1cheese/qs by deleted user 5 years ago
- [New] `parse`: add `allowSparse` option for not collapsing arrays with missing indices Fixes #181. — committed to 1cheese/qs by deleted user 5 years ago
- Issue #181: Updated readme file - added info about allowSparse option — committed to 1cheese/qs by deleted user 5 years ago
- Issue #181 Added option for collapsing arrays with missing indices — committed to 1cheese/qs by deleted user 5 years ago
- [New] `parse`: add `allowSparse` option for collapsing arrays with missing indices Fixes #181. — committed to 1cheese/qs by deleted user 5 years ago
- [New] `parse`: add `allowSparse` option for collapsing arrays with missing indices Fixes #181. — committed to 1cheese/qs by deleted user 5 years ago
- [New] `parse`: add `allowSparse` option for collapsing arrays with missing indices Fixes #181. — committed to 1cheese/qs by deleted user 5 years ago
I’m running into this limitation as well. My situation looks like this, with radio buttons:
So the user chooses a value between 1-3, or nothing at all, for each title. The titles are dynamically generated. In this case, you need the “indexed field names”, like title[0], title[1], etc. to determine which title each value applies to. I can work around this using the trick with hidden fields, but it would be nice if this was supported out of the box!
You can dynamically add
option[]with no numerical indices - there’s zero reason you need them for single options - if you have multiple options, the better choice would be to pick a unique alphanumeric name - for example,description["item_" + n]is a very straightforward choice, is trivial to iterate on the other side.I’ll think more about this option - creating sparse arrays is just not a good idea, so it’d need to respect
arrayLimitand also picknullorundefinedto fill out the empty items.