Rails 5:为集合中的每个子对象渲染一个div

时间:2016-11-30 19:05:48

标签: ruby-on-rails ruby ruby-on-rails-5

我试图想出一种在页面上使用reddit样式嵌套注释的有效方法。我已经按照我想要的方式设置了一切。但是,我很难弄清楚如何以有效的方式呈现评论。请参阅我目前的部分内容:

#_comment_list.html.erb
<div class="comment-list">
  <h2>Comments</h2>
  <ul>
    <% @post.comments.each do |c| %>
        <% byebug %>
      <li><%= c.body %></li>
      <% unless c.is_deleted == true %>
        <%= render partial: "shared/comment_form", :locals => { commentable_type: c.class.name, commentable_id: c.id, post: @post.id } if current_user %>
      <% end %>
      <ul>
        <% c.comments.each do |d| %>
          <li><%= d.body %></li>
          <% unless d.is_deleted == true %>
            <%= render partial: "shared/comment_form", :locals => { commentable_type: d.class.name, commentable_id: d.id, post: @post.id } if current_user %>
          <% end %>
        <% end %>
      </ul>
    <% end %>
  </ul>
</div>

显然,这只会呈现一组儿童评论,如下所示:

Post
  Comment
    Child Comment
    Child Comment
  Comment
  ...

我设计了一个空白,设计明智,如何将孩子评论的孩子渲染多次,因为他们需要嵌套。

Post
  Comment
    Child Comment
      Grandchild Comment
        Great Grandchild Comment
        Great Grandchild Comment
    Child Comment
  Comment
  ...

如果有人能指出我去哪儿方向,我将不胜感激。

以下是有关我的模型和关联的一些信息,如果它有助于提出解决方案。

# Comment.rb
class Comment < ApplicationRecord
  validates_presence_of :body
  # validates :user_id, presence: true

  belongs_to :user
  belongs_to :commentable, polymorphic: true
  has_many :comments, as: :commentable

  def find_parent_post
    return self.commentable if self.commentable.is_a?(Post)
    self.commentable.find_parent_post # semi recursion will keep calling itself until it .is_a? Post
  end
end

# Post.rb
class Post < ApplicationRecord
  validates :user_id, presence: true
  validates :forum_id, presence: true

  belongs_to :user
  belongs_to :forum

  has_many :comments, as: :commentable
end

create_table "comments", force: :cascade do |t|
  t.text     "body"
  t.datetime "created_at",                       null: false
  t.datetime "updated_at",                       null: false
  t.integer  "commentable_id"
  t.string   "commentable_type"
  t.integer  "user_id"
  t.boolean  "is_deleted",       default: false, null: false
end

create_table "forums", force: :cascade do |t|
  t.string   "name"
  t.text     "description"
  t.integer  "user_id"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
  t.index ["user_id"], name: "index_forums_on_user_id", using: :btree
end

create_table "posts", force: :cascade do |t|
  t.string   "title"
  t.text     "description"
  t.integer  "user_id"
  t.integer  "forum_id"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
  t.index ["forum_id"], name: "index_posts_on_forum_id", using: :btree
  t.index ["user_id"], name: "index_posts_on_user_id", using: :btree
end

2 个答案:

答案 0 :(得分:2)

两种方法:

创建一个引用子注释的部分,例如:

# comments/_show.html.erb

<% depth ||= 0 %>
<div class="comment" style="padding-left: <%= 16 * depth %>px;">
  <%= comment.text %>
  <% comment.children.each do |c| %>
    <%= render partial: "comments/show", locals: { comment: c, depth: depth + 1 } %>
  <% end %>
</div>

或者您可以修改您的注释模型以在数据库级别进行自然嵌套,并一次查询/命令/缩进它们。这更复杂,但它非常强大且非常易于使用。

在评论表中添加tree_lefttree_rightdepth列(所有正整数)。索引tree列。

tree_lefttree_right是相互唯一的,包含从1到(记录数* 2)的每个数字。这是一个示例树:

test# select id, text, parent, tree_left, tree_right, depth from comments order by tree_left;
+----+----------------------------+-----------+-----------+------------+-------+
| id | text                       | parent    | tree_left | tree_right | depth |
+----+----------------------------+-----------+-----------+------------+-------+
|  1 | Top Level Comment          |      NULL |         1 |         30 |     0 |
|  2 | Second Level               |         1 |         2 |         29 |     1 |
|  3 | Third Level 1              |         2 |         3 |         20 |     2 |
|  5 | Fourth Level 1             |         3 |         4 |          9 |     3 |
| 12 | Fifth Level 4              |         5 |         5 |          6 |     4 |
| 13 | Fifth Level 5              |         5 |         7 |          8 |     4 |
|  6 | Fourth Level 2             |         3 |        10 |         19 |     3 |
|  8 | Fifth Level                |         6 |        11 |         18 |     4 |
|  9 | Sixth Level 1              |         8 |        12 |         13 |     5 |
| 10 | Sixth Level 2              |         8 |        14 |         15 |     5 |
| 11 | Sixth Level 3              |         8 |        16 |         17 |     5 |
|  4 | Third Level 2              |         2 |        21 |         28 |     2 |
|  7 | Fourth Level 3             |         4 |        22 |         27 |     3 |
| 14 | Fifth Level 6              |         7 |        23 |         24 |     4 |
| 15 | Fifth Level 7              |         7 |        25 |         26 |     4 |
+----+----------------------------+-----------+-----------+------------+-------+

使用depth = 0tree_left = (current largest tree_right + 1)tree_right = (current largest tree_right + 2)插入顶级评论。

使用depth = parent.depth + 1tree_left = parent.tree_righttree_right = parent.tree_right +插入子评论。然后,运行:

UPDATE comments SET tree_left = tree_left + 2 WHERE tree_left >= #{parent.tree_right}
UPDATE comments SET tree_right = tree_right + 2 WHERE tree_right >= #{parent.tree_right}

评论A是评论B的孩子,当且仅当:   A.tree_left > B.tree_leftA.tree_right < B.tree_right

所以它的工作方式是你可以让树中的所有孩子都属于评论&#34; XYZ&#34;使用此查询:

Select * from comments where tree_left >= #{XYZ.tree_left} AND tree_right <= #{XYZ.tree_right} ORDER BY tree_left

要获得评论的所有父母,请使用相反的符号:

Select * from comments where tree_left <= #{XYZ.tree_left} AND tree_right >= #{XYZ.tree_right} ORDER BY tree_left

在条件中包含=可确定是否包含用于生成查询的注释。

tree_left的顺序非常重要,它将它们置于嵌套树顺序中。然后在您的视图中,您可以直接遍历此列表,并按其深度缩进它们。

有关此tree_left和tree_right内容工作原理的更多信息,请查看本文的嵌套集理论部分:http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

答案 1 :(得分:1)

递归就是你要找的东西。这是一个棘手的例子:http://benjit.com/rails/2015/04/01/rendering-partials-with-layouts-recursively/

这是一些示例代码

#_post.html.erb
<%= render partial: 'comment_list', locals: {commenabel: @post} %>

这是递归的部分:

#_comment_list.html.erb
<div>
  <%= commentable.to_s %>
</div>
<ul>
  <% commentable.comments.each do |comment| %>
    <li>
      <%= render partial: 'comment_list', locals: {commentable: comment} %>
    </li>
  <% end %>
</ul>