-
-
Notifications
You must be signed in to change notification settings - Fork 486
How to generate nested unordered list tags with one DB hit
DrewBe121212 edited this page Jan 11, 2014
·
8 revisions
There isn’t any helper that generates nested <ul>, <li> tags for you, but you can create your own off this example.
In your controller:
def index
#one hit to DB, brings back all associated comments
@sketchbook_comments = @artist.sketchbook_comments.order('lft ASC')
end
<div class="comments">
<%= nested_li(@sketchbook_comments) do |comment| %>
<%= comment.comment %> (what goes in <li> tag)
<% end %>
</div>
In your helper (based on the each_with_level class method) put:
def nested_li(objects, &block)
objects = objects.order(:lft) if objects.is_a? Class
return '' if objects.size == 0
output = '<ul><li>'
path = [nil]
objects.each_with_index do |o, i|
if o.parent_id != path.last
# We are on a new level, did we descend or ascend?
if path.include?(o.parent_id)
# Remove the wrong trailing path elements
while path.last != o.parent_id
path.pop
output << '</li></ul>'
end
output << '</li><li>'
else
path << o.parent_id
output << '<ul><li>'
end
elsif i != 0
output << '</li><li>'
end
output << capture(o, path.size - 1, &block)
end
output << '</li></ul>' * path.length
output.html_safe
end
To sort the list first add this helper as well:
def sorted_nested_li(objects, order, &block)
nested_li sort_list(objects, order), &block
end
private
def sort_list(objects, order)
objects = objects.order(:lft) if objects.is_a? Class
# Partition the results
children_of = {}
objects.each do |o|
children_of[ o.parent_id ] ||= []
children_of[ o.parent_id ] << o
end
# Sort each sub-list individually
children_of.each_value do |children|
children.sort_by! &order
end
# Re-join them into a single list
results = []
recombine_lists(results, children_of, nil)
results
end
def recombine_lists(results, children_of, parent_id)
if children_of[parent_id]
children_of[parent_id].each do |o|
results << o
recombine_lists(results, children_of, o.id)
end
end
end
And call it in the view:
<div class="pages">
<%= sorted_nested_li(@pages, :title) do |page| %>
<%= page.title %> (what goes in <li> tag)
<% end %>
</div>