概要
- コメント投稿機能を実装したい
- コメントは全件表示したい
- けど全部表示するのは邪魔だから、最新3件だけ表示して残りは必要なときだけ表示したい
環境
- macOS Catalina 10.15.6
- ruby 2.6.5
- Rails 6.0.3.4
学んだこと
- ActiveRecord
- first : 引数に指定した数だけレコードを取得するメソッド
- offset : 引数に指定した数だけレコードを読み飛ばすメソッド
- HTML
- details : 折りたたみウィジェット (クリックしたら開くオブジェクト) を生成するHTML要素
- summary : detail要素のバーの文字を指定 (上のGIFでいう「全てのコメントを表示」)
今回のコード
- 必要部分のみを抜粋します
- association等は省略
1. itemsコントローラ
- items#show のリクエストを受け取る
- before_action :set_item で商品情報を取得
- before_action :set_comments で商品に紐づくコメントを全件取得 (新着順)
- 全コメントにfirst(3)適用 : 上から3件を取得
- 全コメントにoffset(3)適用 : 上から4件目以降を全取得
- 以上のデータをshow.html.erbに渡す
app/controllers/items_controller.rb
class ItemsController < ApplicationController
before_action :set_item, only: [:show]
before_action :set_comments, only: [:show]
def show
@comment = Comment.new
# 新着コメントを上から3件取得
@comments_latest3 = @item_comments.first(3)
# 新着コメント3件を除く全コメントを取得 (3件以下の場合は空)
@comments_offset3 = @item_comments.offset(3)
end
private
def set_item
# PATHパラメータでitemを取得
@item = Item.find(params[:id])
end
def set_comments
# itemに紐づくcommtnsを新着順で取得
@item_comments = @item.comments.includes(:user).order('created_at DESC')
end
end
2. show.html.erb
- 部分テンプレートで最新3件を表示
- 4件目以降がない場合はそのまま終了
- 4件目以降があった場合は、折りたたみウィジェットの中に4件目以降を全表示
app/views/items/show.html.erb
<%# コメント一覧を表示 %>
<div id="comment-lists">
<%= render partial: 'shared/comment', collection: @comments_latest3 %>
<%# コメントが4件未満の場合はここから表示しない %>
<% if @comments_offset3.any? %>
<details>
<summary>全てのコメントを表示</summary>
<%= render partial: 'shared/comment', collection: @comments_offset3 %>
</details>
<% end %>
</div>
app/views/shared/_comment.html.erb
<div class="comment-box">
<div class="comment-info">
<%# user名を表示 %>
<p class="comment-user"><%= comment.user.nickname %></p>
<%# 投稿時間を表示 %>
<p class="comment-time"><%= l comment.created_at %></p>
</div>
<%# 改行が反映される形でコメントを表示 %>
<p><%= simple_format(h(comment.text)) %></p>
</div>
その他、工夫したこと
- コメントにユーザー情報を載せるときにN+1問題を回避したこと (includes メソッド)
- コメント投稿時刻を日本時間にしたこと (l メソッド)
- コメントに記入した改行が実際の表示に反映されるようにしたこと (simple_format)
- simple_format 使用時に文字をエスケープさせたこと (h オプション)