Search code examples
htmlarraysformsruby-on-rails-4

HTML Form: POST an array of objects


Submitting a class roster. Adding 3 students at once. Each student has first, last, age.

Question: How can we get all of the students in an array of arrays?

students[0] => Array (
  ["first"] => "first name for 0",
  ["last"] => "last name for 0",
  ["age"] => "age for 0"
),
students[1] => Array (
  ["first"] => "first name for 1",
  ["last"] => "last name for 1",
  ["age"] => "age for 1"
), 
...  

Details
For one student:

<input type="text" name="first">  
<input type="text" name="last">  
<input type="text" name="age">  

We can return multiple students in separate arrays like this:

<input type="text" name="students[first][]">  
<input type="text" name="students[last][]">  
<input type="text" name="students[age][]">  

which returns an array of firsts, lasts and ages

students["first"] = [array of first names]
students["last"] = [array of last names]
students["age"] = [array of ages]  

Theoretically we can get all the info for a student by accessing the same index (say "3" for each array).

We do not want to programatically add an index in the form.
Do not want:

<input type="text" name="students[hardcoded_index][first]">  
<input type="text" name="students[hardcoded_index][last]">  
<input type="text" name="students[hardcoded_index][age]">  

If for any reason it matters, we are using Rails for views but can use form helpers or HTML.


Solution

  • tl;dr: Add empty brackets ([]) after students to the input names.

    Fiddling with Rack::Utils.parse_nested_query it seems you can get the payload you want like this:

    <!-- first student -->
    <input type="text" name="students[][first]">
    <input type="text" name="students[][last]">
    <input type="text" name="students[][age]">
    
    <!-- second student -->
    <input type="text" name="students[][first]">
    <input type="text" name="students[][last]">
    <input type="text" name="students[][age]">
    

    Note the empty brackets ([]) after students. This tells Rack you want the students param to be an array. Subsequent params encountered (with the same name) will start a new element.

    POST /myroute?students[][first]=foo&students[][last]=bar&students[][age]=21&students[][first]=baz&students[][last]=qux&students[][age]=19

    Gets parsed like this:

    {"students" => [
      {
        "first" => "foo",
         "last" => "bar",
          "age" => "21"
      },
      {
        "first" => "baz",
         "last" => "qux",
          "age" => "19"
      }
    ]}
    

    Further reading: http://codefol.io/posts/How-Does-Rack-Parse-Query-Params-With-parse-nested-query