forked from stefankroes/ancestry
-
Notifications
You must be signed in to change notification settings - Fork 3
Creating a selectbox for a form using ancestry
yaniviny edited this page May 29, 2011
·
12 revisions
Assuming we have a model Category
- Add a new string field: names_depth_cache
- Add a before_save to your ancestry model:
before_save :cache_ancestry
def cache_ancestry
self.names_depth_cache = path.map(&:name).join('/')
end
- In the controller:
@categories = Category.order(:names_depth_cache).map { |c| ["-" * c.depth + c.name,c.id] }
- In the form:
<%= f.select :parent_id, @categories%>
- In the form:
<%= f.select :parent_id, @categories%>
- In the controller:
Category.all.each { |c| c.ancestry = c.ancestry.to_s + (c.ancestry != nil ? "/" : '') + c.id.to_s
}.sort {|x,y| x.ancestry <=> y.ancestry
}.map{ |c| ["-" * (c.depth - 1) + c.name,c.id]
}.unshift(["-- none --", nil])
The problem with some of the options above (at laest option2) is that it doesn't keep the order of the category. You can bypass this by doing the following:
- In the form:
<%= f.select :parent_id, @categories%>
- In the controller:
@categories = ancestry_options(Category.scoped.arrange(:order => 'name') {|i| "#{'-' * i.depth} #{i.name}" }
def ancestry_options(items)
result = []
items.map do |item, sub_items|
result << [yield(item), item.id]
#this is a recursive call:
result += ancestry_options(sub_items) {|i| "#{'-' * i.depth} #{i.name}" }
end
result
end
The main benefit of the above's implementation is that it's a single SQL call (for efficiency) and the order remains as it should.