Nested Comments from Scratch

Nested comments from scratch

It seems like what you have is one short step away from what you want. You just need to use recursion to call the same code for each reply as you're calling for the original comments. E.g.

<!-- view -->
<div id="comments">
<%= render partial: "comment", collection: @comments %>
</div>

<!-- _comment partial -->
<div class="comment">
<p><%= comment.content %></p>
<%= render partial: "comment", collection: comment.replies %>
</div>

NB: this isn't the most efficient way of doing things. Each time you call comment.replies active record will run another database query. There's definitely room for improvement but that's the basic idea anyway.

Single query for nested comments

The following code should cater to your second need in an efficient way:

CREATE TABLE comment(
comment_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
date INT UNSIGNED,
parent_id INT UNSIGNED DEFAULT NULL,
commend TEXT,
FOREIGN KEY fk_parent(parent_id)
REFERENCES comment(comment_id)
ON UPDATE CASCADE
ON DELETE RESTRICT)

You just want to ensure that a child comment isn't inserted before a parent comment because of the foreign key reference.

However, when it comes to ordering your table by date, the MySQL CREATE TABLE syntax includes no such functionality and I think an effective implementation would be to simply ORDER BY while querying. Bear in mind, however, if you do choose to alter the table at a later stage and order it by the date, that any operations post this alteration will not adhere to this order. Specifically:

ALTER TABLE comment ORDER BY date ASC

will only order the present data in your table and not any subsequent data entering it.

Also, consider using a DATE data type instead of int, depending on your requirement.

Threaded / Nested comments

It is hard to believe that after 8 years of asp.net, no one has bothered to write a simple article or tutorial on how something like threaded comments that are so commonly used on millions of blogs is done. Search engines yield nothing but garbage as usual. Then again, Google is a worthless piece of you know what.

This is why asp.net takes such a long time to learn. Not that it is brain surgery, only the fact that getting beyond the basic stuff present in books is impossible because the information does not exist.

I spent a whole day thinking about this and finally solved the problem using a single table for comments, two stored procedures handling comments and replies separately, a listview for displaying the indented comments exactly where they belong and another listview for the comment form. Best of all, there is no limit to the depth of threads and the comments are cached.

I my comments database table, I created a new column that stores the sorting value calculated in the stored procedures. For comments the sorting value is set equal to its own comment id value and for comment replies the value is set equal to the parent sorting id concatenated with it's own comment id separated by a dot. If a sorting id has no dots, it is a top level comment. One dot equals one level depth, two equals two level depth etc.

So, the only code I had to write was two simple stored procedures and a bit of C# to get the number of dots and assign the appropriate css value for indentation. I did not need multiple tables with foreign keys, parent-child id relationships, complicated code or any of the exotic recommendations commonly suggested by the few who actually bother to answer on forums.

Fast, efficient and works like a charm. Common sense rules!

Nested Comments in C/C++

If the compiler doesn't allow nesting, the first */ will terminate the opening of the multiline comment, meaning the 0 won't be commented out. Written with some spaces:

int nest = /*/*/ 0 * /**/ 1;

resulting in the code

int nest = 0 * 1; // -> 0

If it allows nesting, it will be

int nest = /*/*/0*/**/ 1;

resulting in

int nest = 1;

How to properly render nested comments in rails?

For the first part of your question, you'll probably need some scopes that help you distinguish between "parent comments" (ie comments that aren't replies) and "child comments" (replies).

something like:

class Acomment < ApplicationRecord
belongs_to :answer
belongs_to :user
belongs_to :parent, class_name: "Acomment", optional: true
has_many :replies, class_name: "Acomment", foreign_key: :parent_id, dependent: :destroy

scope :is_parent, where(parent_id: nil)
scope :is_child, where("parent_id IS NOT NULL")
end

You can use these eg below to render only parents in the main loop... with the intention each parent will render its own children.

<% answer.acomments.is_parent.each do |comment| %>
<%= render :partial=>"comment", :object=>comment %>
<% end %>

WRT your second question - I'll need a bit more info - I'll ask in comments above.

After:

So... My guess at what's happening is: you're just getting all the comments (as you mentioned) and they're being displayed in the outer loop (instead of just the parents being displayed int he outer loop) and they're in that order because that's the database-order for the comments.

Nothing is actually being displayed in the inner loop due to a default naming convention in partials.

If you use collection in a partial - Rails will feed each item into the partial... and then name the local variable after the name of the partial. In this case, you are trying to render a partial named "reply", but passing in a set of comments... so the partial is going to look for a variable called "reply".

I'd recommend you just reuse the same "comment" partial that you already use and instead render it this way:

<div>
<%= comment.body %>
<%= link_to "Reply", new_acomment_path(:parent_id => comment, :answer_id => comment.answer) if comment.parent_id.blank? %>

<%= render partial: "comment", collection: comment.replies %>
</div>

It will be recursive (just like Pablo's suggestion) and thus render comment threads by indenting for every set of sub-comments.

Looking at your conversation with pablo... it's possible that parents is a bad name for a scope... it sounds like it might be interfering with a ruby-method named the same thing that traverses up the class hierarchy... so perhaps it's a bad name - as such I'll rename the scope.

Commenting a commented markup (Nested comments)

If you have no </style> and */ tag in comment area use:

<style>
/*
comment
*/
</style>

If you have no </script> and */ tag in comment area use:

<script>
/*
comment
*/
</script>


Related Topics



Leave a reply



Submit