Search code examples
ruby-on-railsrubysimple-formcocoon-gem

Scope associations for a model used multiple times in a single form with simple_form and cocoon


I've found myself in the following semi-normal situation:

class Contract
  has_many :contract_locations, dependent: :destroy
  has_many :locations, through: :contract_locations
end

class ContractLocation
  enum role: { shipper: 0, receiver: 1 }
  belongs_to :contract
  belongs_to :location
end

class Location
  has_many :contract_locations, dependent: :destroy
  has_many :contracts, through: :contract_locations
end

The form in question is the Contract form, which is working fine in the situation where, for each associated location, I select a location and a role. Architecturally, this works, but because I effectively have two Location "types" (:shipper and :receiver), I'd like to have them as two separate parts of the form. So essentially a section of the form with its own "add shipping location" button and another section with its own "add receiving location" button. I'm able to achieve this, but the problem it causes is when the form is being populated from existing relationships. If I submit the form like this:

Screen Shot 2019-03-21 at 1 44 11 PM

And I load the edit form again, the values are filled like this:

Screen Shot 2019-03-21 at 1 45 19 PM

Clearly this is because cocoon is just filling in the associated Locations as it's supposed to, and doesn't distinguish between Locations with specific roles. Are there any scoping features that would allow me to only create these form elements for Locations for some specific scope (as in role: :shipper)?

EDIT: I should note that I've tried using the provided Javascript callbacks, particularly before-insert, but it doesn't look like they trigger when the form is loaded for the first time.


Solution

  • I've solved my issue, though I'm still interested to see if there's a way built-in to Cocoon to solve this.

    My solution is to check the form object in my cocoon partial:

    <%= f.simple_fields_for :contract_locations do |contract_location| %>
      <!-- This check prevents locations of the wrong role being rendered in the wrong form section. -->
      <% if contract_location.object.role == role.to_s %>
        <%= render 'form_location_fields', f: contract_location, role: role %>
      <% end %>
    <% end %>