Rails + ajax でコメントにいいねする機能をつくったのでまとめます。
参考:railsとjsを使ったお手軽「いいね♡機能」
Railsでいいね機能を実装。Ajaxを使い非同期対応。で
✴️私の場合ログインしてるユーザーがいいねするわけではなくオープン(誰でもいいねできる)だったので上記記事にかいてあるユーザーとのアソシエーションはありません。
✴️いいねが追加されていくだけで減らす機能はつくっていません。
ファイル構成
├── comments
│ ├── _comment_lists.html.erb
│ ├── _form.html.erb
│ └── create.js.erb
├── likes
│ ├── _like.html.erb
│ └── create.js
resources :comments ,:only => [:create] do
resources :likes, only: [:create]
end
##前提
Commentテーブルにはlikes_count
カラム(ここでは)がある
Likeテーブルにはcomment_id
カラムがある(アソシエーション)
Likeモデルにcounter_cache
というカウントしてくれるRailsの機能を使う記述をする↓
class Like < ApplicationRecord
belongs_to :comment, counter_cache: :likes_count
end
###コントローラ
class CommentsController < ApplicationController
protect_from_forgery except: :create
def create
@comment = Comment.new(comment_params)
@comment.article_id = session[:top_id]
if @comment.save
@comments = Article.find(session[:top_id]).comments
@comments = []
@comments << @comment
render :comment_lists
else
render :comment_lists
end
end
private
def comment_params
params.require(:comment).permit(:name, :email, :content, :article_id)
end
end
class LikesController < ApplicationController
def create
@comment_id = Comment.find(params[:comment_id])
session[:comment_id] = @comment_id.id
@like = Like.create( comment_id: session[:comment_id])
@likes = Like.where( comment_id: session[:comment_id])
@comment_id.reload #ここでリロードしなくてもいいように更新処理してくれてる
end
end
##ajax
コメント一覧をeachでまわしてコメントを一つずつを見ていきます。
renderでlikeのviewまで飛びます。
そのときにパーシャルやローカル変数を渡す際は書き方に決まりがあるので注意。
comment
のIDが1
の場合("likes_button_<%= comment.id %>")
は("#likes_button_1")
となる
<% if @comments.present? %>
<% @comments.each_with_index do |comment,i| %>
<div class="comment_box">
<div class ="wrap">
<div class = "wrap-l">
<div class = "id" id = "id_<%= i+1 %>" ><%= i+1 %></div>
<%= comment.name %>
</div>
<div class = "date"><%= comment.created_at.strftime("%Y/%m/%d %H:%M")%></div>
</div>
<div class ="wrap">
<div class ="content"><%= comment.content %></div>
<div id="likes_button_<%= comment.id %>">
<%= render :partial =>'likes/like', :locals => { comment: comment } %>
</div>
</div>
</div>
<% end %>
いいねのviewまで来ました。
ここで使っている変数comment
は↑_comment_lists.html.erb
で指定したものを使っています。
<%= button_to comment_likes_path( :comment_id => comment.id), id: "like-button", remote: true do %>
<div class="btn-social-circle btn-social-circle">
<%= fa_icon("fas star") %>
<span>
<%= comment.likes_count %>
</span>
</div>
<% end %>
likes_controller.rb
の一行目にかいてある @comment_id = Comment.find(params[:comment_id])
がここでつかわれている
@comment_id
が1
の場合("#likes_button_<%= @comment_id.id %>")
は("#likes_button_1")
となる
$("#likes_button_<%= @comment_id.id %>").html('<%= j(render :partial =>'like', :locals => { comment: @comment_id }) %>')
_like.html.erb
でのcommentの取得の仕方はローカル変数を指定して使えるようにしていて、create.js
ではlikes_controller.rb
に記述した内容が反映されているようだ。
IDに番号を指定しないと3番目のコメントのいいねボタンを押してもDBにはちゃんと3番目のコメントのいいねに+1した値が入ってるのに見た目上リロードする前の増えたいいねは一番上のコメントのいいねに反映されてしまうので識別しなきゃいけないことに注意だ。
##番外編
リプライがあったらリプライを表示、なかったらアラートを表示するっていう仕様について
ajaxの中でRailsの文法を使って条件分岐できます
$(function () {
// <!-- リプ一覧欄開閉 -->
$('#reply_lists_id_<%= i+1 %>').hide();
<% if comment.replies.present? %>
$('#comment_user_<%= i+1 %>').click(function () {
$('#reply_lists_id_<%= i+1 %>').toggle();
<% else %>
$('#comment_user_<%= i+1 %>').click(function () {
window.alert('リプライがありません。');
<% end %>
});
});