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
So, I was not happy with the solution I saw most often, which was to generate a pseudo-index for new elements, either on the server or in client-side JS. This feels like a kludge, especially in light of the fact that Rails/Rack is perfectly capable of parsing lists of items so long as they all use empty brackets ([]) as the index. Here's an approximation of the code I wound up with:
# note that this is NOT f.fields_for.
fields_for 'parent[children_attributes][]', child, index: nil do |f|
f.label :name
f.text_field :name
# ...
end
Ending the field name prefix with [], coupled with the index: nil option, disables the index generation Rails so helpfully tries to provide for persisted objects. This snippet works for both new and saved objects. The resulting form parameters, since they consistently use [], are parsed into an array in the params:
params[:parent][:children_attributes] # => [{"name" => "..."}, {...}]
The Parent#children_attributes= method generated by accepts_nested_attributes_for :children deals with this array just fine, updating changed records, adding new ones (ones lacking an "id" key), and removing the ones with the "_destroy" key set.
I'm still bothered that Rails makes this so difficult, and that I had to revert to a hardcoded field name prefix string instead of using e.g. f.fields_for :children, index: nil. For the record, even doing the following:
f.fields_for :children, index: nil, child_index: nil do |f| ...
...fails to disable field index generation.
I'm considering writing a Rails patch to make this easier, but I don't know if enough people care or if it would even be accepted.
EDIT: User @Macario has clued me in to why Rails prefers explicit indices in field names: once you get into three layers of nested models, there needs to be a way to discriminate which second-level model a third-level attribute belongs to.