cocoon icon indicating copy to clipboard operation
cocoon copied to clipboard

Simple form wrappers break with new entries

Open KelseyDH opened this issue 5 years ago • 2 comments

Great library! Unfortunately it appears that when using simple_form wrappers that cocoon doesn't correctly pick up on these wrappers when inserting new elements. I tested my problem with the demo app and replicated the problem.

E.g. in the coccoon_simple_form_demo if we replace:

      = f.simple_fields_for :people do |person|

with a built-in simple_form wrapper like horizontal_form

     = f.simple_fields_for :people, wrapper: :horizontal_form, wrapper_mappings: { select: :floating_labels_select} do |person|

the first form entry will render with the correct simple_form wrappings, but subsequent entries won't. (See photo.)

Screen Shot 2020-07-11 at 3 19 25 PM

KelseyDH avatar Jul 11 '20 22:07 KelseyDH

Ha, cool, never specified the wrapper on the simple_fields_for before (just on the complete form). Let me look into that.

nathanvda avatar Jul 12 '20 07:07 nathanvda

Looking into this, it's clear the custom SimpleForm wrappers aren't being passed. The major difference being that the div is not form-label-group and the select does not have custom-select, which I assume SimpleForm adds from its wrapper options hash.

The question is how to get cocoon to pass along the arguments of these options hashes from simple_fields_for.

At first, I suspected the issue might be this line of code for render_association:

      render_options = received_render_options.dup

E.g. could .deep_dup instead help retrieve the nested wrapper_mappings options hash? But that may not be it.

Rather, it looks more that the issue is with how render_association builds the new form object:

    def render_association(association, f, new_object, form_name, received_render_options={}, custom_partial=nil)
      partial = get_partial_path(custom_partial, association)
      render_options = received_render_options.dup
      locals =  render_options.delete(:locals) || {}
      ancestors = f.class.ancestors.map{|c| c.to_s}
      method_name = ancestors.include?('SimpleForm::FormBuilder') ? :simple_fields_for : (ancestors.include?('Formtastic::FormBuilder') ? :semantic_fields_for : :fields_for)
      f.send(method_name, association, new_object, {:child_index => "new_#{association}"}.merge(render_options)) do |builder|
        partial_options = {form_name.to_sym => builder, :dynamic => true}.merge(locals)
        render(partial, partial_options)
      end
    end

It appears the render_options / received_render_options hash is passed along with the simple_fields_for in the form here to generate the new form element. So I came up with the idea of trying to pass the wrapper options again to my nested form, but that didn't work e.g.:

  <div id="derivatives">
    <div class="row">

      <%= f.simple_fields_for :derivatives,
        wrapper: :floating_labels_form,
        wrapper_mappings: {
          select: :floating_labels_select
        } do |d| %>

        <div class="col">
          <%= render 'derivative_fields', f: d %>
        </div>
      <% end %>
      <div class="links">
        // Attempting to pass wrapper arguments here.
        <%= link_to_add_association 'add derivative', f, :derivatives, {wrapper: :floating_labels_form, wrapper_mappings: {select: :floating_labels_select} } %>
      </div>
    </div>
  </div>

KelseyDH avatar Aug 13 '20 00:08 KelseyDH