admin管理员组

文章数量:1317906

I have an HTML form with an array of checkboxes (using [] naming). I need to be able to process this with express. I'm using body-parser.

The problem is that unchecked checkboxes don't submit a value, and at the same time, body-parser seems to remove "holes" in arrays by simply packing the values into an array in order of indices, but ignoring the indices themselves. (Update: Actually it looks like qs is the culprit).

Consider this full example, which displays a form and responds with a JSON dump of the submitted data:

Install:

npm install express body-parser

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 is:

option[1]:1
text[0]:
text[1]:
text[2]:

Yet body-parser collapses the option array and produces the following in req.body:

{"option":["1"],"text":["","",""]}

As you can see, text has all three, but option has only one item. Similarly, if I were to check option[0] and option[2], the request body would look like:

option[0]:1
option[2]:1
text[0]:
text[1]:
text[2]:

But it would be parsed to:

{"option":["1","1"],"text":["","",""]}

I lose all information about which checkbox was checked.

My question is, how do I do this? What I want to happen is, e.g.:

  • With checkbox[1] checked:

    {"option":[null,"1",null],"text":["","",""]}
    
  • With checkbox[0] and checkbox[2] checked:

    {"option":["1",null,"1"],"text":["","",""]}
    

I'm not actually married to null and "1", I just need falsey and truthy.

Also, it is important that I not lose information about how many checkboxes should be in the array. For example, if I were to give each checkbox a unique value, I suppose I could translate "option":["0","1"] into an array of boolean values, except I would lose the knowledge that the array is of size 3 (with the 3rd value false in that case) -- although I guess I could add e.g. a hidden input like numberOfCheckboxes=3, but... this kind of mapping is cumbersome and I'd like to avoid it if possible.

I have an HTML form with an array of checkboxes (using [] naming). I need to be able to process this with express. I'm using body-parser.

The problem is that unchecked checkboxes don't submit a value, and at the same time, body-parser seems to remove "holes" in arrays by simply packing the values into an array in order of indices, but ignoring the indices themselves. (Update: Actually it looks like qs is the culprit).

Consider this full example, which displays a form and responds with a JSON dump of the submitted data:

Install:

npm install express body-parser

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 is:

option[1]:1
text[0]:
text[1]:
text[2]:

Yet body-parser collapses the option array and produces the following in req.body:

{"option":["1"],"text":["","",""]}

As you can see, text has all three, but option has only one item. Similarly, if I were to check option[0] and option[2], the request body would look like:

option[0]:1
option[2]:1
text[0]:
text[1]:
text[2]:

But it would be parsed to:

{"option":["1","1"],"text":["","",""]}

I lose all information about which checkbox was checked.

My question is, how do I do this? What I want to happen is, e.g.:

  • With checkbox[1] checked:

    {"option":[null,"1",null],"text":["","",""]}
    
  • With checkbox[0] and checkbox[2] checked:

    {"option":["1",null,"1"],"text":["","",""]}
    

I'm not actually married to null and "1", I just need falsey and truthy.

Also, it is important that I not lose information about how many checkboxes should be in the array. For example, if I were to give each checkbox a unique value, I suppose I could translate "option":["0","1"] into an array of boolean values, except I would lose the knowledge that the array is of size 3 (with the 3rd value false in that case) -- although I guess I could add e.g. a hidden input like numberOfCheckboxes=3, but... this kind of mapping is cumbersome and I'd like to avoid it if possible.

Share Improve this question edited Dec 12, 2016 at 1:54 Jason C asked Dec 12, 2016 at 0:57 Jason CJason C 40.4k15 gold badges135 silver badges198 bronze badges 1
  • Related issues posted: github./expressjs/body-parser/issues/211, github./ljharb/qs/issues/181 – Jason C Commented Dec 13, 2016 at 19:03
Add a ment  | 

2 Answers 2

Reset to default 7

My approach requires no javascript on client side. Add hidden fields as many as your checkboxes with same names

body parser will parse checked items as array and string others

I meant

<input type="hidden" name="option[0]" value="0">
<input type="hidden" name="option[1]" value="0">
<input type="hidden" name="option[2]" value="0">
<input type="checkbox" name="option[0]" value="1">
<input type="checkbox" name="option[1]" value="1">
<input type="checkbox" name="option[2]" value="1">

If your option[1] is checked then body parser will parse it like

{option:['0', ['0', '1'], '0']}

And here is the modifier

req.body.option = req.body.option.map(item => (Array.isArray(item) && item[1]) || null);

so now body will be

{option: [null, '1', null]}

The simplest solution (not the best) is that you can add hidden input's with different ids and then check them when the check-boxes on the page get unchecked.

<input type="checkbox" name="option[0]" value="1">
<input type="checkbox" name="option[1]" value="1">
<input type="checkbox" name="option[2]" value="1">

<input type="checkbox" class="hidden" name="hiddenOption[0]" value="1">
<input type="checkbox" class="hidden" name="hiddenOption[1]" value="1">
<input type="checkbox" class="hidden" name="hiddenOption[2]" value="1">

And before submit:

$('input[name^=option]').each(function () {      
  if(!this.checked) {
    var name = "input[name=\'hiddenOption" + this.name.replace('option', '') + "\']";
    console.log(name);
    $(name).prop('checked', true);
  }
});

And then based on that you can figure out which ones are not ticked.

https://plnkr.co/edit/mJCbtgQnQudHGrUzAz3A?p=preview

本文标签: javascriptExpress bodyparser handling checkbox arrays on formsStack Overflow