16

I'm using Rails to auto-magically create child objects based on a complex set of nested attributes. Therefore, I need the parameters to be nested in a very particular way. Obviously I realize I can construct them however I want with JS, but I'd like the order of the form to automatically help with the construction. For context, I have 2 columns, represented by 2 <td>s. Each column can either create a new record or edit an existing record. Of course, when an existing record is to be modified, the id of the record must be passed.

The rendered HTML is as follows:

<td width="50%" style="padding-right:3%" class="logistic-details" data-type="logistics" data-typelogistics="delivery" data-instructions="test instructions" data-id="1" data-amount="20">
  <span class="area-to-inject-amount-inputs" data-object="type_logistics" data-type="logistics" data-typelogistics="delivery">
    <input class="labeler-response" name="type_logistics_attributes[][id]" type="hidden" value="1">
    <input class="labeler-response" name="type_logistics_attributes[][instructions]" type="text" value="test instructions">
  </span>
</td>

<td width="50%" style="padding-right:3%" class="logistic-details" data-type="logistics" data-typelogistics="pickup" data-instructions="" data-id="" data-amount="0">
  <span class="area-to-inject-amount-inputs" data-object="type_logistics" data-type="logistics" data-typelogistics="pickup" data-actioned="charged">
    <input type="hidden" name="type_logistics_attributes[][type_of_logistics]" value="pickup">
    <input class="injected-amount-input" type="number" min="0" max="" placeholder="Amount" name="type_logistics_attributes[][charged_amounts_attributes][][amount]" value="20">
    <span class="area-to-inject-type-of-amount">
      <input type="hidden" name="type_logistics_attributes[][charged_amounts_attributes][][type_of_amount]" value="logistics">
    </span>
    <input class="labeler-response" name="type_logistics_attributes[][instructions]" type="text" placeholder="Enter address and instructions">
  </span>                      
</td>

In this case, the first <td> is modifying an existing record with id of 1, while the second <td> is providing the parameters to create a new record. When a new record is created, child charged_amounts are also created. Thus, these are the parameters I would expect:

"type_logistics_attributes"=>[
  {"id"=>"1", "instructions"=>"test instructions"},
  {"type_of_logistics"=>"pickup", "charged_amounts_attributes"=>[{"amount"=>"40", "type_of_amount"=>"logistics"}], "instructions" => "123 Fake street"}
]

Instead, I am getting the below:

"type_logistics_attributes"=>[
  {"id"=>"1", "type_of_logistics"=>"pickup", "instructions"=>"test instructions", "charged_amounts_attributes"=>[{"amount"=>"40", "type_of_amount"=>"logistics"}]}, 
  {"instructions"=>"123 Fake street"}
]

Somehow, the <td> boundary isn't working and the child charged_amount attributes have somehow been lumped into the first <td> existing record modification.

Thanks!

james
  • 3,989
  • 8
  • 47
  • 102
  • 1
    Can you post also your RoR code which are you using to generate this markup? – Vucko Jul 28 '16 at 13:39
  • 1
    Can you provide us with the _raw POST data_ submitted by your browser? The easiest way to get this will be from the Network pane of the browser's developer tools. – zwol Jul 28 '16 at 13:45

3 Answers3

3

It is not <td> or any similar HTML element that makes a "boundary" for inputs, it is the <form> element only. All inputs that are within a <form> tag are sent by the browser as parameters when submitting the form. You probably have both columns in one <form> and that is why parameters from both columns are intermixed in the params.

Matouš Borák
  • 15,606
  • 1
  • 42
  • 53
  • Sorry not sure you understood the question. I DO want both parameters in one form. But I need them to nest differently than they currently are – james Jun 04 '16 at 20:16
2

The only way I know of by which you can get both parameters in one form but nested differently is that if you provide some id to the new input field. That is what distinguishes the elements of the type_logistics_attributes array.

I had a similar need and I used ryanb's nested form. When I use it for the same purpose as yours, what it does is, it provides a random id to the new input field. That is how it is differentiated.

Hope this guides you in the right direction.

mansoor.khan
  • 2,309
  • 26
  • 39
-1

I'm not sure that there is a way to do it specifically as you want, but you can make params look like this:

"type_logistics_attributes"=>[
  {"id"=>"1", "instructions"=>"test instructions"},
  {"id"=>"1", "type_of_logistics"=>"pickup", "charged_amounts_attributes"=>[{"amount"=>"40", "type_of_amount"=>"logistics"}], "instructions" => "123 Fake street"}
]

You can achieve it by adding additional hidden input repeating id here:

<td width="50%" style="padding-right:3%" class="logistic-details" data-type="logistics" data-typelogistics="pickup" data-instructions="" data-id="" data-amount="0">
  <span class="area-to-inject-amount-inputs" data-object="type_logistics" data-type="logistics" data-typelogistics="pickup" data-actioned="charged">
    <input name="type_logistics_attributes[][id]" type="hidden" value="1">
    <input type="hidden" name="type_logistics_attributes[][type_of_logistics]" value="pickup">
    <input class="injected-amount-input" type="number" min="0" max="" placeholder="Amount" name="type_logistics_attributes[][charged_amounts_attributes][][amount]" value="20">
    <span class="area-to-inject-type-of-amount">
      <input type="hidden" name="type_logistics_attributes[][charged_amounts_attributes][][type_of_amount]" value="logistics">
    </span>
    <input class="labeler-response" name="type_logistics_attributes[][instructions]" type="text" placeholder="Enter address and instructions">
  </span>                      
</td>

Rails has a rule for handling this: it takes the attributes from left to right in the querystring (or top to bottom in your HTML), and creates a new object each time it sees a repeated attribute.

Roman Kovtunenko
  • 437
  • 2
  • 12