I have a parent, which I can add multiple children and I would like to add a clone button with cocoon in each child.
Following the next solution I have made this code:
These are the models:
class MantenimientoSetup < ApplicationRecord
has_many :actuation_setups, inverse_of: :mantenimiento_setup, validate: true, autosave: true, dependent: :destroy
accepts_nested_attributes_for :actuation_setups, allow_destroy: true, reject_if: :all_blank
end
class ActuationSetup < ApplicationRecord
belongs_to :mantenimiento_setup, inverse_of: :actuation_setups
end
I have got to add the clone button out of child partial... parent and child form
but I need this button for each new child nested, like this: enter image description here
These are the parent and child partials:
<!-- views/mantenimiento_setups/_form.html.erb -->
<%= form_for @mantenimiento_setup do |f| %>
...
...
...
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Configuraciones</h4>
</div>
<div class="panel-body mantenimiento-setup-element">
<%= f.fields_for :actuation_setups do |act_setup_builder| -%>
<%= render 'actuation_setup_fields', f: act_setup_builder %>
<%= link_to_add_association 'Clone', f, :actuation_setups, data: {"association-insertion-node" => ".mantenimiento-setup-element", "association-insertion-method" => "append"}, wrap_object: Proc.new {|d| d = act_setup_builder.object.dup; d}, class: "btn btn-info btn-sm" %>
<% end -%>
</div>
<div class="panel-footer">
<%= link_to_add_fields 'Add new setup', f, :actuation_setups, {mantenimiento_setup: f.object}, {class: "btn btn-success", nested_form: "/mantenimiento_setups/actuation_setup_fields" }%>
</div>
</div>
<%= save_button %>
<% end %>
<!-- views/mantenimiento_setups/_actuation_setup_fields.html.erb -->
<div class='row fields'>
<div class="col-md-3 actuation-type-div-selector">
...
...
...
<div class="col-md-1">
<label class="control-label invisible">...</label><br/>
<%= link_to_remove_fields "Remove", f%>
</div>
</div>
If I paste the clone button in the child partial, close to remove button...
<!-- views/mantenimiento_setups/_actuation_setup_fields.html.erb -->
<div class='row fields'>
<div class="col-md-3 actuation-type-div-selector">
...
...
...
<div class="col-md-1">
<label class="control-label invisible">...</label><br/>
<%= link_to_remove_fields "Remove", f%>
</div>
<div class="col-md-1">
<label class="control-label invisible">...</label><br/>
<%= link_to_add_association 'Clone', f, :actuation_setups, data: {"association-insertion-node" => ".mantenimiento-setup-element", "association-insertion-method" => "append"}, wrap_object: Proc.new {|d| d = act_setup_builder.object.dup; d}, class: "btn btn-info btn-sm" %>
</div>
</div>
It returns this error because the association does not exist due to I am calling from child to child association:
Association actuation_setups doesn't exist on ActuationSetup
How can I add a clone button for each child?
The link_to_add_association
needs the form-object (f
) from the parent (where the associations are defined). So at the nested level, you need to be aware of the parent-form-object.
Also: we would only want to render the Clone
button if the record already exists. Maybe I should explain that better: the form and all Clone
-partials are rendered on the server, and thus will not copy fields if they are edited/changed in the form. Is that clear? To be able to do that, we would need more javascript-code, and this might also be a very valid approach, maybe simpler: trigger the link_to_add_association
link and in the cocoon:after-insert
event we can prefill the fields copied from the to-be-cloned item, if there is one. But, as said, that would be a pure javascript solution.
To remain close to your initial suggestion, your views would look like (I did not copy the entire view)
<!-- views/mantenimiento_setups/_form.html.erb -->
<%= f.fields_for :actuation_setups do |act_setup_builder| -%>
<%= render 'actuation_setup_fields', f: act_setup_builder, parent_f: f %>
<% end -%>
and then in your nested form you can write:
<!-- views/mantenimiento_setups/_actuation_setup_fields.html.erb -->
<div class='row fields'>
<div class="col-md-3 actuation-type-div-selector">
<div class="col-md-1">
<label class="control-label invisible">...</label><br/>
<%= link_to_remove_fields "Remove", f%>
</div>
<% if local_assigns.has_key?(:parent_f) %>
<div class="col-md-1">
<%= link_to_add_association 'Clone', parent_f, :actuation_setups, data: {"association-insertion-node" => ".mantenimiento-setup-element", "association-insertion-method" => "append"}, wrap_object: Proc.new {|d| d = act_setup_builder.object.dup; d}, class: "btn btn-info btn-sm" %>
</div>
<% end %>
</div>
(my erb is a little rusty, I always write haml or slim as I find them more readable and less typing-work, so there might be some typos).
In short: for each existing :actuation_setup
we hand down the parent_f
parameter (the parent-form-object), and in the nested child we check if this parameter was given, and if so we know it was an existing child, so then we add the clone-link, and use the parent-form-object to be able to add a new nested child as a copy of the existing/given nested child.