web-dev-qa-db-ja.com

Railsでネストされたコメントを適切にレンダリングする方法は?

ネストされたコメントをRails 5アプリで正しく機能させようとしていますが、多くの問題が発生しています。

質問と回答のサイトを構築しています。回答に属するコメントがあり、他のコメントに属するコメントがあるコメントモデルがあります。

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パーシャル内に、コメントと返信リンクを表示し、コメントへの返信用にパーシャルをレンダリングします。

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

私は2つの問題に直面しています:

1)最初のパーシャルをレンダリングすると、コメントがレンダリングされるだけでなく、同時に返信もレンダリングされます(コメントでもあるため)。

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

どんな助けでも大歓迎です。

6
Tony M

Tarynが書いたように、最初にparent_idのないコメントをフィルタリングする必要があります。つまり、コメントは回答に対する第1レベルのコメントです。

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>

コメント間の階層が明確になるように、インデントを付ける必要があります。

4
Pablo

質問の最初の部分では、「親コメント」(つまり、返信ではないコメント)と「子コメント」(返信)を区別するのに役立つスコープがおそらく必要になります。

何かのようなもの:

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

以下の例を使用して、メインループ内の親のみをレンダリングできます...各親が独自の子をレンダリングすることを意図しています。

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

2番目の質問をWRT-もう少し情報が必要です-上記のコメントで質問します。

後:

だから...何が起こっているのか私の推測は:あなたは(あなたが言ったように)すべてのコメントを受け取っているだけで、それらは(親だけが外側のループに表示されるのではなく)外側のループに表示されていますそしてそれらはコメントのデータベース順序であるため、この順序で再実行します。

パーシャルのデフォルトの命名規則により、実際には内部ループに何も表示されていません。

パーシャルでcollectionを使用する場合-Railsは各アイテムをパーシャルにフィードします...次に、パーシャルの名前にちなんでローカル変数に名前を付けます。この場合、 「reply」という名前のパーシャルをレンダリングしようとしていますが、コメントのセットを渡しています...そのため、パーシャルは「reply」という変数を探します。

すでに使用しているのと同じ「コメント」パーシャルを再利用し、代わりに次のようにレンダリングすることをお勧めします。

<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の提案と同じように)、サブコメントのセットごとにインデントすることでコメントスレッドをレンダリングします。

Pabloとの会話を見ると...parentsがスコープの不適切な名前である可能性があります...クラス階層をトラバースするのと同じ名前のRubyメソッドに干渉しているようです...おそらくそれは悪い名前です-そのため、スコープの名前を変更します。

1
Taryn East