LoginSignup
0
1

More than 3 years have passed since last update.

Railsアプリに非同期通信のいいね機能を実装

Posted at

はじめに

Rialsアプリに非同期通信のいいね機能を実装しました。
方針としては、部分テンプレートを作成してレスポンスをJS形式で返すことで画面の一部のみを更新するというものです。備忘録としてまとめたいと思います。

実装イメージ

ezgif.com-gif-maker (9).gif

  • 非同期でいいねといいねの解除ができる
  • いいねされたらカウントに反映される

開発環境

  • macOS Catalina
  • Ruby 2.6.5
  • Ruby on Rails 5.2

前提

  • userとpost(投稿)モデルを作成済み
  • fontawesomeを導入済み
  • jQueryを導入済み

目次

1.モデルとアソシエーション
2.ルーティング
3.コントローラー
4.view

1. モデルとアソシエーション

モデルとアソシエーションの構成は以下の通りです。

スクリーンショット 2021-04-17 0.15.09.png
ではfavoriteモデルを作成します。

ターミナル
$ rails g model favorite user:references post:references 
$ rails db:migrate

次にアソシエーションを組みます。

user.rb
  has_many :posts, dependent: :destroy
  has_many :favorites, dependent: :destroy

  # すでにいいねしているかを判定するメソッド
  def already_favorited?(post)
    self.favorites.exists?(post_id: post.id)
  end

already_favorited?メソッドを定義します。
これはもし投稿にいいねしていたら解除のリンクを表示させ、解除していたらいいねリンクを表示させる条件分岐のために記述しています。

post.rb
  belongs_to :user
  has_many :favorites, dependent: :destroy
favorite.rb
  belongs_to :user
  belongs_to :post

2. ルーティング

いいねは投稿ひとつひとつに紐付いているのでルーティングはネストさせて記述します。
ネストさせることでアソシエーション先のレコードのidをparamsに追加してコントローラーに渡せるようになります。
(今回の場合はfavoriteのidを取得できる)

routes.rb
 resources :posts, only: [:index, :new, :create, :show, :destroy] do
    resource :favorites, only: [:create, :destroy]
 end

3. コントローラー

favorites_controller.rbではcreateとdestroyアクションを定義します。

favorites_controller.rb
class FavoritesController < ApplicationController
  before_action :set_post

  def create
    @favorite = Favorite.create(user_id: current_user.id, post_id: @post.id)
    @favorite.save
  end

  def destroy
    @favorite = Favorite.find_by(user_id: current_user.id, post_id: @post.id)
    @favorite.destroy
  end

  private
  def set_post
    @post = Post.find(params[:post_id])
  end

end

set_post
・before_actionを設定することで、アクション実行前にどの投稿に対するものなのかを判断するためにidを取得します。

posts_controller.rb
  def show
    @post= Post.find(params[:id])
  end

4. view

非同期通信をするために_favorite.html.erbという部分テンプレートを作成します。
更にcreateとdestroyアクション実行時にページの一部を更新するためにcreate.js.erbdestroy.js.erbというファイルを作成します。
JS形式でレスポンスするためにjs.erbという拡張子になっています。
ディレクトリ構成は以下の通りです。

views
|-posts
|   |-show.html.erb
|-favorites
    |-_favorite.html.erb
    |-create.js.erb
    |-destroy.js.erb

まずは、投稿詳細ページのviewから記述します。

posts/show.html.erb
<div id="favorite_area_<%= post.id %>", class="favoriteArea">
  <%= render partial: "favorites/favorite", locals: { post: @post } %>
</div>

divタグにfavorite_area_<%= post.id %>というidを付与しています。
これはどの投稿に対するものなのかを判別するために記述しています。

またrenderメソッドで_favorite.html.erbファイルを呼び出しています。
localsオプションではpostコントローラーで定義した@postの変数を部分テンプレートのなかでpostとして使用できるように定義しています。

次に部分テンプレートです。

favorites/_favorite.html.erb
<% if current_user.already_favorited?(post) %>
  <%= link_to post_favorites_path(post), method: :delete, class: "goodLink", remote: true do %>
    <i class="fas fa-heart"></i>
  <% end %>
<% else %>
  <%= link_to post_favorites_path(post), method: :post, class: "goodLink", remote: true do %>
    <i class="far fa-heart"></i>
  <% end %>
<% end %>
<p class="favoriteCount"><%= post.favorites.count %></p>

user.rbで定義したalready_favorited?メソッドを使用しています。
link_toにはremote: tureを記述することで非同期通信をしますという意味になります。これでHTML形式ではな先程作成したjs.erbファイルを返す挙動になります。
最後の行の<%= post.favorites.count %>はcountメソッドを使用していいねされている数を表示しています。

最後にjs.erbファイルの編集です。

favorites/create.js.erb
<!-- #favorite_area_<%= @post.id %>この部分のHTMLだけ、renderで部分的に更新するという処理 -->
$("#favorite_area_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
favorites/destroy.js.erb
<!-- #favorite_area_<%= @post.id %>この部分のHTMLだけ、renderで部分的に更新するという処理 -->
$("#favorite_area_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");

意味としてはfavorite_area_<%= @post.id %>というidを付与したdivタグの中身を_favorite.html.erbの内容で更新するという内容になります。

以上で非同期のいいね機能を実装できていると思います!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1