10

Let's say I have to edit a batch of objects of the same type on the same page:

//-jade
form(action='', method='POST')
    for each message_id in messages_ids
        input(type='text', name='message', id='#{message_id}')
        input(type='text', name='author',  id='#{message_id}')
    input(type='submit', value='Send')

I know I wont be able to process such form on backend 'cause of id -- it wont be sent to backend. However, is there a way to do it? I'd like to get something like this on backend:

//js
for (var i = 0; i <= req.body.message.length; i++) {
    console.log (
        'ObjectID: ' + req.body.message[i].id, //-? null, just to show what I'm trying to get
        'Message: '  + req.body.message[i],
        'Author: '   + req.body.author[i]
    );
}

It's pseudocode (it wont work). So, any ideas?

P.S. I'm getting how to do this without AJAX

f1nn
  • 6,989
  • 24
  • 69
  • 92

1 Answers1

26

For bodyParser.urlencoded, if you set the extended option to true, attribute names of the format property[nestedProperty] will be interpreted by the bodyParser middleware as:

{ property: nestedPropert: $value }

Just be sure to initialize the middleware like so:

app.use(bodyParser.urlencoded({ extended: true });

Afterwards, change the above form declaration to have the message property be an object, instead of a string value, like so:

form(action='', method='POST')

    - for (var i = 0; i < messages_ids.length; i++)
        - var message_id = messages_ids[i]
        //- so we're treating each index like a property of `message`
        input(type='text', name='messages[#{i}][message]')
        input(type='text', name='messages[{#{i}}][author]')
        input(type='hidden', name='messages[#{i}][id]', value='#{message_id}')

    input(type='submit', value='Send')

And then, on the server side, request.body.messages would be an object, that looks like:

{
  "messages": {
    "1": {
      "message": $message1,
      "author": $author1,
      "id": $id1
    },
    "2": {
      "message": $message2,
      "author": $author2,
      "id": $id2
    } /* , ... */
  }
}

And then, you can easily convert request.body.messages into an array:

var messages = Array.prototype.slice.call(request.body.messages);

And now, you should be able to access each elements like so (note: I prefer a functional style, but I'll keep it consistent with yours):

for (var i = 0; i < messages.length; i++) {
  console.log({
    'ObjectId' + messages[i].id,
    'Message' + messages[i].message,
    'Author' + messages[i].author
  });
}

P.S.: if you're wondering of a functional style, then here it is:

messages.forEach(function (message) {
  console.log(
    'ObjectId' + message.id,
    'Message' + message.message,
    'Author' + messages.author
  );
});

Edit: special thanks to @eye_mew for pointing out that we need to set extended to true.

Sal Rahman
  • 4,607
  • 2
  • 30
  • 43
  • 1
    Simple and awesome, that's what I've been looking for, thanks! – f1nn Sep 06 '13 at 05:46
  • 1
    Not a problem. I have also updated the answer to make it cleaner. – Sal Rahman Sep 06 '13 at 05:49
  • 5
    Does this still work with express 4.*? I've tried a similar approach but my array inputs get passed as strings: `{ 'user[last_name]': 'asd','user[first_name]': 'asd' }` – Andrei Rosca Aug 24 '15 at 13:07
  • 10
    @AndreiRosca, had the same issue. Fixed it by specifying `{extended: true}` option like: `app.use(bodyParser.urlencoded({ extended: true }));` – eye_mew Feb 14 '16 at 20:59
  • it doesn't work if I use it like this `name='messages[][message]'`. I am using jquery to add message objects to the form and I don't have the index number. – Ali Sherafat Jul 04 '17 at 15:41
  • @AliSherafat did you ever get this to work without specifying the index numbers in the form? – Jonah Feb 12 '18 at 09:14
  • This answered my question. My array items were coming over as simple string keys rather than an array of objects. I had extended set to false in my `bodyParser.urlencoded()` method. I copy + pasted the code from a previous tutorial. I would like to ask, why would anyone want extended to be set to false? It doesn't seem to do any harm to set it to true, I'm wondering why the tutorial I went through had us set it to false. Took me a while to figure out why the request body wasn't breaking out my arrays into actual arrays of objects. Setting extended to true worked! Thank you. – Matthew Wolman Apr 22 '20 at 18:15