nathanvda / cocoon

Dynamic nested forms using jQuery made easy; works with formtastic, simple_form or default forms
http://github.com/nathanvda/cocoon
MIT License
3.08k stars 385 forks source link

Cocoon doesn't build third relationship #557

Closed Kutomore closed 5 years ago

Kutomore commented 5 years ago

Hi, I'm the same guy from #556 experiencing a different issue. Right now everything is working as expected, except that when I add a new Frame, it doesn't build said frame's Image, for example, on the controller I build the Frame like @order.frames.build.build_image and then when I call my form:

<%= form_for @order do |form| %>
  <div id="frames-form">
        <%= form.fields_for :frames do |frames_form| %>
          <%=render 'frame_fields', f: frames_form %>
        <% end %>
      </div>
      <p class="col-md-11 button-to-add-frame">
        <%=link_to_add_association 'Add Frame',
                                   form,
                                   :frames,
                                   class: 'button btn btn-primary col-md-12'
        %>  
    </p>
<% end %>

Which in turn renders:

<fieldset>
  <legend class="col-md-4 control-label"><strong>Frame:</strong></legend>
  <div class="form-group">
    <%= f.label :type_of_frame, class: "col-md-4 control-label" %>
    <div class="col-md-12">
      <%= f.select :type_of_frame, options_for_enum(Frame.new, :type_of_frame), {}, {class: 'form-control'} %>
    </div>
  </div>
  <div class="form-group">
    <%= f.label :frame_status, class: "col-md-4 control-label" %>
    <div class="col-md-12">
      <%= f.select :frame_status, options_for_enum(Frame.new, :frame_status), {}, {class: 'form-control'} %>
    </div>
  </div>
  <div class="form-group">
    <%= f.label :is_broken, class: "col-md-3 control-label" %>
    <%=f.check_box :is_broken %>
  </div>
  <div class="form-group">
    <%= f.label :frame_value, class: "col-md-4 control-label" %>
    <div class="col-md-12">
      <%= f.text_field :frame_value %>
    </div>
  </div>
  <div class="col-md-6 control-label">
    <button type="button" class="btn btn-secondary button-to-add-image" data-toggle="modal" data-target="#image-modal">
      Adicionar imagem
    </button>
  </div>
</fieldset>
<br>
<div class="col-md-4 control-label">
    <button type="button" class="btn btn-danger button-to-remove-frame">Remove Frame</button>
</div>
<div class="modal fade" id="image-modal" tabindex="-1" role="dialog" aria-labelledby="image-modal-label" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="image-modal-label">Add Image</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <%= f.fields_for :image do |image_form| %>
          <%=render partial: 'image_form', locals: {image_form: image_form} %>
        <% end %>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

Which, once I click the "Add Image" button, opens a modal with the following image form:

<div class="image-form">
  <label>Add a new image</label>
  <div class="field">
    <%= image_form.label :description %>
    <%= image_form.text_field :description %>
  </div>
  <div class="field">
    <%= image_form.label :art %>
    <%= image_form.file_field :art %>
  </div>
</div>

This works for the first frame, which has an instantiated image. But when it comes to the frames that Cocoon adds it doesn't instantiate a new Image for the Frame, so when I try to open the modal all I see is: image An empty modal body. I assume it is empty because rails tries to create fields_for :image, and since :image is nil it doesn't render anything.

If I build it on the controler before, or if, like an edit, the frame already exists it works fine.

So I assume I have to instantiate the Image manually, how would I go about solving this?

nathanvda commented 5 years ago

This is a valid question, it is not a new problem, it is described in the documentation, but I can understand it is maybe not immediately clear when reading that there is an option that could help you.

But in short: the link_to_add_association builds the object to render the form, and in most default cases this is enough. But for cases like this, cocoon has the option :wrap_object to add a specific initialisation.

So in your case you would have to do something like:

<%=link_to_add_association 'Add Frame',
                                   form,
                                   :frames,
                                   class: 'button btn btn-primary col-md-12',
                                   partial: 'frames_form',
                                   form_name: 'frames_form',
                                   wrap_object: Proc.new { |frame| frame.build_image; frame } 
                              %>
Kutomore commented 5 years ago

Works flawlesly, thanks man.