Search code examples
ruby-on-railsfields-for

Rails 5 fields_for only sends last params


I have been hitting my head against a brick wall so it is time to seek smarter people.

I am trying to create multiple records of one model using form_tag and fields_for. I have been following all the help/issues/guides I can find but it doesn't seem to work for me. I am wondering if it something that changed going to Rails 5 but more likely it is me.

Basically I want a new/create version of the task system listed at the bottom of the api page, similar to this guys puppy creator.

The "new" page loads fine with as many records as I like, so that part is ok but it doesn't seem to be creating a collection to send through, it is just overriding and thus sending through the last set of params so only creating one record.

What I have.

# routes
resources :container_returns

controller

# container returns controller
def new
  @containers = Container.where(id: params[:container_ids])
  @container_returns = []
  @containers.each do |container|
    @container_returns << ContainerReturn.new(
      {
        container_id: container.id,
        quantity: container.amount,
        uom: container.uom,
        material_restriction_id: container.material_restriction_id
      }
    )
  end  
end

view

# new.html.erb
<%= form_tag container_returns_path, method: :post do %>

  <% @container_returns.each do |container_return| %>
    <%= fields_for 'returns[]', container_return, hidden_field_id: true do |cr| %>

      <div class="field">
        <%= cr.label :container_id %>
        <%= cr.number_field :container_id %>
      </div>

      <div class="field">
        <%= cr.label :material_restriction_id %>
        <%= cr.number_field :material_restriction_id %>
      </div>

      <div class="field">
        <%= cr.label :quantity %>
        <%= cr.text_field :quantity %>
      </div>

      <div class="field">
        <%= cr.label :uom %>
        <%= cr.text_field :uom %>
      </div>

    <% end %>        
  <% end %>

  <%= submit_tag "lots of returns" %>

<% end %>

which submits

# params submitted
Started POST "/container_returns" for 127.0.0.1 at 2018-10-19 11:00:48 +0200
Processing by ContainerReturnsController#create as HTML
Parameters: {
  "utf8"=>"✓", "authenticity_token"=>[removed],
  "returns"=>{"container_id"=>"405", "material_restriction_id"=>"", "quantity"=>"100.0", "uom"=>"kg"}, "commit"=>"lots of returns"
}

hopefully it is just something stupid that I missed.

UPDATE: if I add an index to the form it now believes me that my objects are different and sends through all the params I need.

<% @container_returns.each_with_index do |container_return, index| %>
  <%= fields_for 'returns[]', container_return, index: index do |cr| %>
  [...]

Solution

  • as mentioned in the update, if I add an ID to the initial create it builds the correct array that I was expecting. What I also found was if I send through an index position that also works.

    <% @container_returns.each_with_index do |container_return, index| %>
      <%= fields_for 'returns[]', container_return, index: index do |cr| %>
      [...]
    

    gives me what I was expecting

    Parameters: {
      "returns"=>{"0"=>{"container_id"=>"400",...},
                  "1"=>{"container_id"=>"401",...},
                  etc.
                 },
      "commit"=>"lots of returns"
    }