如何在 rails 中正确呈现嵌套评论?

How to properly render nested comments in rails?

我试图让嵌套评论在 rails 5 应用程序中正常工作,运行 遇到了很多困难。

我正在构建一个问答网站,我有一个 Acomment 模型,其中有属于答案的评论,还有属于其他评论的评论:

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
end

我想显示所有评论,然后显示所有对评论的回复,以及对回复的回复等。但是我不知道如何使嵌套正常工作。

在我看来,在每个循环中,我正在渲染 _comment.html.erb 部分:

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

然后,在我的 _comment.html.erb 局部中,我正在显示评论、回复 link,然后呈现评论回复的局部:

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

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

这是 _reply.html.erb 部分:

<%= reply.body %>
<%= link_to "Reply", new_acomment_path(:parent_id => reply, :answer_id => reply.answer) %>

我 运行 遇到两个问题:

1) 当我渲染第一个部分时,它不仅渲染了评论,还同时渲染了回复(因为它们也是评论)。

2) 当我渲染第二部分(回复)时,它们没有嵌套在它们应该嵌套的评论中。一切都乱了。

编辑: 我希望看到这个:

Comment 1 
  Comment on Comment 1 
    Comment on Comment on Comment 1 
Comment 2 

但我看到的是:

Comment 1 
Comment on Comment 1 
Comment 2 
Comment on Comment 1 
Comment on Comment on Comment 1 
Comment on Comment on Comment 1

如有任何帮助,我们将不胜感激。

对于问题的第一部分,您可能需要一些范围来帮助您区分 "parent comments"(即不是回复的评论)和 "child comments"(回复)。

类似于:

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

您可以使用下面的这些示例在主循环中仅渲染 parents...目的是每个 parent 将渲染其自己的 children.

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

关于你的第二个问题 - 我需要更多信息 - 我会在上面的评论中提问。

之后:

所以...我对发生的事情的猜测是:您只是获得了所有评论(如您所提到的)并且它们显示在外循环中(而不只是 parents在外循环中显示)并且它们是按该顺序排列的,因为那是评论的 database-order。

由于 partials 中的默认命名约定,内部循环中实际上没有显示任何内容。

如果您在部分中使用 collection - Rails 会将每个项目提供给部分...然后以部分名称命名局部变量。在这种情况下,您正在尝试呈现一个名为 "reply" 的部分,但传递了一组注释...因此该部分将寻找一个名为 "reply".

的变量

我建议您只重复使用您已经使用的相同 "comment" 部分,而不是这样渲染它:

<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>

它将是递归的(就像 Pablo 的建议一样),因此通过缩进每组 sub-comments.

来呈现评论线程

查看您与 pablo 的对话...parents 可能是范围的错误名称...听起来它可能会干扰同名的 ruby-method遍历 class 层次结构...所以这可能是个坏名字 - 因此我将重命名范围。

正如 Taryn 所写,您首先需要过滤没有 parent_id 的评论,这意味着它们是对答案的第一级评论。

views/answers/show.html.erb

<%= @answer.text %>
<h4>Comments</h4>
<% @answer.acomments.parents.each do |comment| %>
  <%= render :partial=>"comment", locals: { comment: comment } %>
<% end %>

那么你需要一个递归的方法:

views/acomments/_comment.html.erb

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

  <% comment.replies.each do |reply|
    <%= render :partial => "comment", locals: { comment: reply } %>
  <% end %>
</div>

您需要提供一些缩进,以便清楚注释之间的层次结构。