-
Notifications
You must be signed in to change notification settings - Fork 1
29 Displaying Threaded Comments
Dave Strus edited this page May 7, 2015
·
1 revision
It's time to display nested comments. First, we'll want to change the default scope in the Comment
model.
app/models/comment.rb
default_scope { order('lft ASC, created_at ASC') }
Next, we need a helper to nest ordered lists (<ol>
) in our view based on the way that the awesome_nested_set
gem (which acts_as_commentable_with_threading
uses under-the-hood) stores nesting data.
Create a new helper module called NestablesHelper
. I'm going to punt and give you the entire contents of the helper. Feel free to examine this more on your own.
app/helpers/nestables_helper.rb
module NestablesHelper
def nested_ol(objects, options = {}, &block)
objects = objects.order(:lft) if objects.is_a? Class
return '' if objects.size == 0
if options[:id].present?
output = "<ol id=\"#{options.fetch(:id)}\">"
else
output = '<ol>'
end
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></ol>'
end
output << '</li>'
else
path << o.parent_id
output << '<ol>'
end
elsif i != 0
output << '</li>'
end
output << "<li data-id=\"#{o.id}\" id=\"#{o.class.to_s.underscore}_li_#{o.id}\" >" + capture(o, path.size - 1, &block)
end
output << '</li></ol>' * path.length
output.html_safe
end
def sorted_nested_ol(objects, order, &block)
nested_ol 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)
return false unless children_of[parent_id]
children_of[parent_id].each do |o|
results << o
recombine_lists(results, children_of, o.id)
end
end
end
We'll invoke the nested_ol
helper method to display comments in posts/show. The following replaces the entire <ol id="comments">
element.
app/views/posts/show.html.erb
<%= nested_ol @post.comment_threads, id: 'comments' do |comment| %>
<div class="tagline" title="<%= comment.created_at %>">
<%= comment.user.username %>
<%= time_ago_in_words comment.created_at %> ago
</div>
<section class="body">
<%= comment.body %>
</section>
<ul class="actions">
<li><%= link_to 'reply', new_comment_path(parent_id: comment.id, format: 'js'), remote: true, method: :get %></li>
</ul>
<div id="reply-form-<%= comment.id %>" style="display: none">
</div>
<% end -%>
How does it look? If everything is working, it's time to commit.
$ git add .
$ git commit -m "Displayed threaded comments."