Rails 5:为集合中的每个子对象渲染一个div
Rails 5: Render a div for each child object in a collection
我正在尝试找出一种在页面上使用 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
两种方法:
为子评论创建一个引用自身的部分,例如:
# 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>
或者您可以修改 Comment 模型以在数据库级别具有自然嵌套,然后 query/order/indent 它们一次全部嵌套。这个比较复杂,但是非常强大,而且非常好用。
在您的评论中添加 tree_left
、tree_right
和 depth
列 table(均为正整数)。索引 tree
列。
tree_left
和 tree_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 = 0
、tree_left = (current largest tree_right + 1)
和 tree_right = (current largest tree_right + 2)
插入顶级评论。
使用 depth = parent.depth + 1
、tree_left = parent.tree_right
和 tree_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_left
,以及 A.tree_right < B.tree_right
。
所以它的工作方式是您可以通过以下查询获取属于评论 "XYZ" 的树中的所有子项:
Select * from comments where tree_left >= #{XYZ.tree_left} AND tree_right <= #{XYZ.tree_right} ORDER BY tree_left
.
要获取评论的所有 PARENTS,请使用相反的符号:
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/
递归就是你要找的。这是一个简单的例子: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>
我正在尝试找出一种在页面上使用 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
两种方法:
为子评论创建一个引用自身的部分,例如:
# 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>
或者您可以修改 Comment 模型以在数据库级别具有自然嵌套,然后 query/order/indent 它们一次全部嵌套。这个比较复杂,但是非常强大,而且非常好用。
在您的评论中添加 tree_left
、tree_right
和 depth
列 table(均为正整数)。索引 tree
列。
tree_left
和 tree_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 = 0
、tree_left = (current largest tree_right + 1)
和 tree_right = (current largest tree_right + 2)
插入顶级评论。
使用 depth = parent.depth + 1
、tree_left = parent.tree_right
和 tree_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_left
,以及 A.tree_right < B.tree_right
。
所以它的工作方式是您可以通过以下查询获取属于评论 "XYZ" 的树中的所有子项:
Select * from comments where tree_left >= #{XYZ.tree_left} AND tree_right <= #{XYZ.tree_right} ORDER BY tree_left
.
要获取评论的所有 PARENTS,请使用相反的符号:
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/
递归就是你要找的。这是一个简单的例子: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>