As others have noted, [] must contain the key for new entries, because otherwise it mixes the hash with the array type. You can set this using the child_index option in the_for fields.
f.fields_for :items, Item.new, child_index: "NEW_ITEM"
I usually do this with object_id to make sure it is unique in case there are several new elements.
item = Item.new f.fields_for :items, item, child_index: item.object_id
An abstract helper method is used here. This assumes that there is a partial name item_fields that it will display.
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 that. The arguments are: link name, parent form template, and association name in the parent model.
<%= link_to_add_fields "Add Item", f, :items %>
And here are a few CoffeeScript for listening to the click event on this link, inserting fields and updating the identifier of the object 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()
This code is taken from this episode of RailsCasts Pro , which requires a paid subscription. However, there is a complete working example, free on GitHub .
Update: I want to indicate that adding a child_index placeholder child_index not always necessary. If you do not want to use JavaScript to dynamically insert new records, you can create them in advance:
def new @project = Project.new 3.times { @project.items.build } end <%= f.fields_for :items do |builder| %>
Rails will automatically add an index for new entries so that it just works.