いいね、コメントの非同期通信のやり方
はじめに
非同期通信でメンターさんに助けてもらって自分なりの言葉でまとめてみました。
目次
- 前提条件
- コントローラー
- ビューファイル
- jsファイル
前提
- いいね機能の実装済
- コメント機能実装済
- deviseで複数アカウント作成しているためpublicになっています。
コントローラー
class Public::FavoritesController < ApplicationController
before_action :authenticate_user!
def create
@post = Post.find(params[:post_id])
@favorite = current_user.favorites.create(post_id: @post.id)
# redirect_to request.referer
end
def destroy
@post = Post.find(params[:post_id])
@favorite = current_user.favorites.find_by(post_id: @post.id)
@favorite.destroy
# redirect_to request.referer
end
end
解説
同期処理ではローカル変数でも問題なく動きましたが、Ajaxリクエストで処理する場合にはcreate.js.erb や destroy.js.erbが対応するビューファイルになります。その時にインスタンス変数を参照しているためインスタンス変数にする必要があります。
非同期通信にする際はredirect(画面遷移)ではなく、サーバーの方で処理されるため、redirectは削除する(上記は比較のためコメントアウトのままにしています。)
補足
同期処理ではローカル変数で処理できた理由はredirect_to request.refererでパスに対してリクエストが送信されます。リダイレクト先のアクションが新しいビューをレンダリングするため、この時点でローカル変数は不要です。そのため問題なく動きました。
しかし、Ajaxの利用の際はJavaScriptのテンプレートの利用となります。その時にJavaScriptのテンプレートからはローカル変数は参照できないため、インスタンス変数に変える必要があります。変えないとidが見つからないとエラーがでてしまいます。
ビューファイル
<% if current_user.favorite_by?(post) %>
<%= link_to post_favorite_path(post), method: :delete, remote: true, style: "color: red;" do %>
<i class="fa-solid fa-heart"></i>
<%= post.favorites.count %>
<% end %>
<% else %>
<%= link_to post_favorite_path(post), method: :post, remote: true do %>
<i class="fa-regular fa-heart"></i>
<%= post.favorites.count %>
<% end %>
<% end %>
解説
remote: trueがAjaxを使用するために必要なため非同期処理の際は記述してください。
<div id="favorite_<%= post.id %>">
<%= render 'public/favorites/favorite_btn', post: post %>
</div>
解説
<div id="favorite_<%= post.id %>">について
これは「いいね」をするにあたって一つだけ「いいね」がしたいからです。このpost.idが入っていないとこの投稿に「いいね」をしたいのにfavoriteモデルのすべてにいいねがされる可能性があるためです。そのため、この投稿だけに「いいね」をするとidを使って明示的に表現しています。
post.idはRuby on RailsのActive Recordの記法です。具体的には、postという変数がRuby on Railsのモデルオブジェクトであり、そのオブジェクトの属性(この場合はid)にアクセスしています。""(ダブルクォーテーション)の中はHTMLと同じです。その為、HTMLに文法を落とし込むには<%= %>で囲む必要があります。
また、<div id ~にしているのはclassでも問題ないのですが、classは複数回利用できてしまうので同ページに「いいね」は一つだけの機能なのでidにしています。
jsファイル
$('#favorite_<%= @post.id %>').html("<%= j(render 'public/favorites/favorite_btn', post: @post) %>");
$('#favorite_<%= @post.id %>').html("<%= j(render 'public/favorites/favorite_btn', post: @post) %>");
解説
ここで同じフォルダにjs.erbのファイルをつくります。この時のファイル名はアクション名になります。createとdestroyアクションが呼ばれるからです。
呼び出しのときはクリックする→
ここで本来favoriteコントローラーdef create ~ endが呼び出されますが、この時ビューファイルにremote: trueがあるとcreate.js.erbが呼び出されます。このcrete.js.erbがビュー代わりになります。→
jsファイルが呼び出された際はjsファイルに記述がある箇所のみ変更されrenderで呼び出されます。その後showやindexなどにさしこまれて表示されます。
Discussion