I have implemented a nested attribute form using the nested attributes Railscast as a guide. As a result, the user can click an icon to dynamically add "child" rows to my view.
Unfortunately, I can only make this work for the last icon in my view (illustrated here). This icon is generated in my view, but the others are generated in the partial which is used to render each row.
Is it possible to do this? If so, what is the best approach?
Here is my latest attempt.
Sheet has_many
Slots. In the sheet edit view, I use a sheet form builder (sheet
) to render my slot partial and also pass it to a helper link_to_add_fields
which renders a link which will generate a new row when clicked (this part works fine). You'll notice I am also attempting to pass sheet
to the partial so that I can call link_to_add_fields
from there but this is where it breaks down:
The view - edit.html.haml
:
= sheet.fields_for :slots do |builder|
= render 'slots/edit_fields', f: builder, sheet:sheet
= link_to_add_fields image_tag("plus.jpg", size:"18x18", alt:"Plus"), sheet, :slots, 'slots/edit'
The partial - _edit_fields.html.haml
:
- random_id = SecureRandom.uuid
.row.signup{:id => "edit-slot-#{random_id}"}
.col-md-1
%span.plus-icon
= link_to_add_fields image_tag("plus.jpg", size:"18x18", alt:"Plus"), sheet, :slots, 'slots/edit'
%span.minus-icon
= image_tag "minus.jpg", size:"18x18", alt:"Minus"
.col-md-2= f.text_field :label
... other fields ...
The helper method:
def link_to_add_fields(name, f, association, partial)
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(partial.to_s.singularize + "_fields", f: builder, name: name)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
I get undefined local variable or method 'sheet'
on the call to the helper from the partial. Basically, I need the sheet (parent) form builder to be available on each link for the helper to work. Or I need to give up on this approach and use AJAX (also tried that).
UPDATE
After debugging a bit, it is clear that sheet
is getting passed down to the partial. The root issue is that I seem to be setting up an endless recursion:
- Partial invokes
link_to_add_fields
so that my+
icon can serve as the "add child" link. link_to_add_fields
renders the partial so that the fields can be generated when the+
icon is pressed.
The other issue I am running into is that when the original children are rendered, they get sequential indexes in the attribute collection (0, 1, 2,...). So, even if I figure out a way to render new child rows among the originals, I'm not sure how I will be able to maintain the order of children when the form is submitted without a lot of jQuery gymnastics or something.
+
button, but add a way of positioning the elements? Like: jqueryui.com/sortable I believe this will solve your problem and add more functionality. I've done it manually in the past. But there is a Rails cast: railscasts.com/episodes/147-sortable-lists – Oubliette+
buttons add a row but it adds it at the bottom? And you want it in a row above the+
clicked? If so, hook into thenested:fieldAdded
js event that is fired from the gem. You should be able to get the target of the event. It'sindex-1
is where you want the new row which will be the last row in the list. – Oubliette+
button at the end of the list works (the one rendered from the edit view). I can't make the ones from inside the partial work at all. Also, when a row is added using that+
button, the+
in that new row doesn't work. – Icariafields_for
block. You could hack around it so that those buttons trigger the single button the gem is expecting. You can then use the index of the button clicked to insert your new row. – Oubliette+
buttons). – Icaria