Built a very basic rails app to manipulate nested attributes using cocoon and the add and remove links work great. However, it wasn't too long until I wanted to alter the underlying content of what was inserted, say in response to another field changing the list of option values in an included select tag. It appears that the contents to be added are contained in an 'a' tag data element (data-association-insertion-template). I can quite easily change the select options for all included lines via jQuery but changing the behavior of the link_to_add_association is beyond me.
Here are snippets of my example:
_form.html.erb
<div>
<strong>Entries:</strong>
<div id="entries" style="border: thin solid">
<%= f.fields_for :entries do |oi| %>
<%= render "entry_fields", f: oi %>
<% end %>
<div class="links">
<%= link_to_add_association 'Add Entry', f, :entries, {id: 'cocoon-add-entry'} %>
</div>
</div>
</div>
_entry_fields.html.erb
<div class="nested-fields">
<%= f.label :item_id %>
<%= f.select :item_id, @items.collect {|i| [i.style, i.id]}, {include_blank: true}, {selected: :item_id, multiple: false} %>
<%= f.label :decoration_id, 'Decoration' %>
<%= f.select :decoration_id, @decorations.collect { |d| [ d.name, d.id ] }, {include_blank: true}, {selected: :decoration_id, multiple: false, class: 'decoration'} %>
<%= f.label :color %>
<%= f.text_field :color %>
<%= f.label :size_id %>
<%= f.select :size_id, @sizes.collect { |s| [ s.name, s.id ] }, {include_blank: true}, {selected: :size_id, multiple: false} %>
<%= f.label :number %>
<%= f.number_field :number, value: 1, min: 1 %>
<%= f.check_box :_destroy, hidden: true %>
<%= link_to_remove_association "Remove Entry", f %>
</div>
orders.coffee
ready = ->
$('.customer').change ->
$.ajax
url: '/orders/change_customer'
data: { customer_id : @value }
$(document).ready(ready)
$(document).on('turbolinks:load', ready)
order_controller.rb
def change_customer
@decorations = Decoration.joins(:logo).where('logos.customer_id = ?', params[:customer_id])
respond_to do |format|
format.js
end
end
change_customer.js.erb
// update all existing entry decorations with new customer driven options
<% new_decor = options_from_collection_for_select(@decorations, :id, :name) %>
var new_decor_options = "<option value='' selected='selected'></option>" + "<%=j new_decor %>";
$('.decoration').html(new_decor_options);
// now need to change $('#cocoon-add-entry').attr('data-association-insertion-template, ???);
// or regenerate link entirely - but don't have required data to do so here (form builder from original)
I have tried to manipulate the template data string directly via js str.replace but that is one ugly regular expression because of the unescapeHTML and htmlsafe operations done to make it an attribute in the first place. And, that approach doesn't smell good to me. I have been slowly working through the cocoon view_helpers and javascript but nothing seems to fit or I don't seem to have the right methods/data values to build a replacement link. Suggestions?
BTW, kudos for cocoon gem.
After a lot of gnashing of teeth, I have cobbled together a potential solution. I haven't decided if I will let this go into production yet because of the limitations but thanks to combining several different SO questions and answers, the following works:
change_customer.js.erb
// update all existing entry decorations with new customer driven options
<% new_decor = options_from_collection_for_select(@decorations, :id, :name) %>
var new_decor_options = "<option value='' selected='selected'></option>" + "<%=j new_decor %>";
$('.decoration').html(new_decor_options);
// update the Add Entry link to capture new decorations set.
// Note use of ugly hack to recreate 'similar' form.
// Also note that this will only work for new order; will have to revise for edit.
'<%= form_for(Order.new) do |ff| %>'
$('#cocoon-add-entry').replaceWith("<%=j render partial: 'add_entry_link', locals: {f: ff} %>");
'<% end %>'
_add_entry_link.html.erb
<%= link_to_add_association 'Add Entry', f, :entries, {id: 'cocoon-add-entry', data: {'association-insertion-method' => 'after'}} %>