0

I have a Restaurant model that has many DishCategory's and also has many Dish's through DishCategory's. So for instance, the category Dinner would be a number of dishes associated with it. To help build the form I'm using the cocoon gem.

I'm familiar with setting up the view code for a traditional nested forms for but now that I'm going one level deeper I'm not sure how to write and where to put the inputs for dishes. Do I setup another simple_fields_for block for dishes inside my partial? Following the docs this is how things are currently setup:

Models

class Restaurant < ActiveRecord::Base
  has_many :dish_categories, dependent: :destroy
  has_many :dishes, through: :dish_categories, dependent: :destroy

  accepts_nested_attributes_for :dish_categories, :reject_if => :all_blank, :allow_destroy => true
end

class DishCategory < ActiveRecord::Base
  belongs_to :restaurant

  has_many :dishes, dependent: :destroy

  accepts_nested_attributes_for :dishes, :reject_if => :all_blank, :allow_destroy => true
end

class Dish < ActiveRecord::Base
  belongs_to :dish_category
end

Restaurant Form

<%= simple_form_for(@restaurant) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :name %>
  </div>

  <div class="form-inputs dish-category-inputs">
    <%= f.simple_fields_for :dish_categories do |f| %>
      <%= render 'dish_category_fields', f: f %>
    <% end %>
  </div>

  <div class='add-dish-category'>
    <%= link_to_add_association 'Add a Dish Category', f, :dish_categories %>
  </div>

  <div class="form-actions">
    <%= f.button :submit %>
  </div>
<% end %>

Dish Category Fields

<div class="dish-category-form nested-fields">
    <%= f.input :name %>

    <%= link_to_remove_association "Remove Category", f %>
</div>

3 Answers 3

1

So it looks like I did need to create an additional partial. From what I understand, the third parameter in the links_to_add_association expects a partial associated with the model for the form you wish to nest.

Dish Category Fields

<div class="dish-category-form nested-fields">
    <%= f.input :name %>

    <%= link_to_remove_association "Remove Category", f %>
</div>

<div class="form-inputs dish-category-inputs">
  <%= f.simple_fields_for :dishes do |f| %>
    <%= render "dish_fields", f: f %>
  <% end %>
</div>

<div class='add-dish'>
    <%= link_to_add_association "Add Dish", f, :dishes %>
</div>

Dish Fields

<div class="dish-category-form nested-fields">
    <%= f.input :name %>

    <%= link_to_remove_association "Remove Dish", f %>
</div>

NOTE:

When it comes to the restaurants controller code it may trip people up in regards to whitelisting your params. Its important that you place dishes_attributes within dish_categories_attributes in order for everything to save properly:

Restaurants Controller

class RestaurantsController < ApplicationController
  ...
  private
    def restaurant_params
      params.require(:restaurant).permit(:name, dish_categories_attributes: [:id, :restaurant_id, :name, :_destroy, dishes_attributes: [:id, :dish_category_id, :name, :description, :_destroy]])
    end
end
Sign up to request clarification or add additional context in comments.

Comments

0

Yes you need to use simple_fields_for for dishes in your partial. And don't forget to use accepts_nested_attributes_for :dishes in the DishCategory model.

Comments

0

I had the same question, as I struggled to get cocoon working with two levels of has_many association. In my case, the issue ultimately stemmed from using cocoon in combination with Trailblazer's Reform form objects. I'm posting an answer in case others face the same problem.

The problem (here: https://github.com/nathanvda/cocoon/blob/master/lib/cocoon/view_helpers.rb#L95) is that cocoon bases the nested fields for an associated object on a pure instance of the model, rather than that model wrapped in the Reform form object. (Not a criticism, just the way it's designed.) That's fine until you want to render fields for an association on that nested object, because the setup for the nested attributes is defined in your Reform form object, not your model.

Essentially f.object in your form partial is the actual model, not the form object. Unless the model itself has accept_nested_attributes configured, fields_for won't treat the attribute as a nested_attributes_association and the html markup won't name the fields in such a way that reform-rails can parse them into properly nested parameters.

The solution for me was using wrap_object to ensure the record cocoon uses is the model wrapped in the relevant form object, like this:

<%= link_to_add_association 'Add Supporting Image', f, :images, partial: 'web_app/report/supporting_image_fields', force_non_association_create: true, wrap_object: Proc.new {|image| Report::WebAppCreation::ImageWithoutHotspotsForm.new(image) } %>

(There's probably a better way to get the relevant Reform form but this gives the idea).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.