Reputation: 5764
I am trying to build a UI for updating a model ("Profile") that has a many to many relationship with another model ("Category"). The "Category" model has a self referential relationship with itself for "Sub Categories".
In my simple_form I want to display the categories as checkboxes with their sub categories nested below them as check boxes.
In my current code all I have is this for the association field:
= f.association :categories, as: :check_boxes, collection: @categories
I am just retrieving the top level categories into the variable @categories.
I'm not sure where to go from here. What is the best way to do this?
Upvotes: 1
Views: 1347
Reputation: 76774
Ancestry
We've done this before using the ancestry
gem:
The problem you have is your self-referential association won't give you the scope required to create a "real" nested dropdown; it has to be able to consider all the nested data.
Instead, what we did was to firstly employee the ancestry
gem, and then use a partial
and helper
to get the nested dropdown affect:
#app/models/category.rb
Class Category < ActiveRecord::Base
has_ancestry
end
Display
If you store the dependent data like that, it allows you to create a partial
-based nested effect:
#app/views/admin/categories/index.html.erb
<%= render partial: "category", locals: { collection: collection } %>
#app/views/categories/_category.html.erb
<!-- Categories -->
<ol class="categories">
<% collection.arrange.each do |category, sub_item| %>
<li>
<!-- Category -->
<div class="category">
<%= link_to category.title, edit_admin_category_path(category) %>
<%= link_to "+", admin_category_new_path(category), title: "New Categorgy", data: {placement: "bottom"} %>
</div>
<!-- Children -->
<% if category.has_children? %>
<%= render partial: "category", locals: { collection: category.children } %>
<% end %>
</li>
<% end %>
</ol>
Dropdown
#app/helpers/application_helper.rb
Class ApplicationHelper
def nested_dropdown(items)
result = []
items.map do |item, sub_items|
result << [('- ' * item.depth) + item.name, item.id]
result += nested_dropdown(sub_items) unless sub_items.blank?
end
result
end
end
This will allow you to call:
= f.input :categories, as: :select, collection: nested_dropdown(@categories)
Upvotes: 2