TAGS :Viewed: 12 - Published at: a few seconds ago

[ Creating a nested two level form in Rails ]

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>

Answer 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

Answer 2


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.