How to submit multiple NEW items via Rails 3.2 mass-assignment

前端 未结 8 1505
醉梦人生
醉梦人生 2020-12-04 06:33

I have a pretty standard use-case. I have a parent object and a list of child objects. I want to have a tabular form where I can edit all the children at once, as rows in th

8条回答
  •  庸人自扰
    2020-12-04 07:10

    As others have mentioned, the [] should contain a key for new records because otherwise it is mixing a hash with an array type. You can set this with the child_index option on fields_for.

    f.fields_for :items, Item.new, child_index: "NEW_ITEM" # ...
    

    I usually do this using the object_id instead to ensure it is unique in case there are multiple new items.

    item = Item.new
    f.fields_for :items, item, child_index: item.object_id # ...
    

    Here's an abstract helper method that does this. This assumes there is a partial with the name of item_fields which it will render.

    def link_to_add_fields(name, f, association)
      new_object = f.object.send(association).klass.new
      id = new_object.object_id
      fields = f.fields_for(association, new_object, child_index: id) do |builder|
        render(association.to_s.singularize + "_fields", f: builder)
      end
      link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
    end
    

    You can use it like this. The arguments are: the name of the link, the parent's form builder, and the name of the association on the parent model.

    <%= link_to_add_fields "Add Item", f, :items %>
    

    And here is some CoffeeScript to listen to the click event of that link, insert the fields, and update the object id with the current time to give it a unique key.

    jQuery ->
      $('form').on 'click', '.add_fields', (event) ->
        time = new Date().getTime()
        regexp = new RegExp($(this).data('id'), 'g')
        $(this).before($(this).data('fields').replace(regexp, time))
        event.preventDefault()
    

    That code is taken from this RailsCasts Pro episode which requires a paid subscription. However, there is a full working example freely available on GitHub.

    Update: I want to point out that inserting a child_index placeholder is not always necessary. If you do not want to use JavaScript to insert new records dynamically, you can build them up ahead of time:

    def new
      @project = Project.new
      3.times { @project.items.build }
    end
    
    <%= f.fields_for :items do |builder| %>
    

    Rails will automatically insert an index for the new records so it should just work.

提交回复
热议问题