Help us understand the problem. What is going on with this article?

Rails 非同期でいいね機能(Ajax)

はじめに

いいねの非同期の代表格はTwitterですね。
Twitterと同じように投稿に対してのいいね機能を非同期通信を用いて実装します。

実装

Railsの基礎の部分は説明は省き、コマンドのみを下記に記述します。
jQuery, Font Awesomeは各自使えるようにしてください。

$ rails new favorite_ajax

$ cd favorite_ajax

ログイン認証はdeviseを使用。

Gemfile
...(省略)
gem 'devise'
$ bundle install

$ rails g devise:install

$ rails g devise User

投稿機能はscaffoldを使用。

$ rails g scaffold Post title:string user_id:integer

いいねのためのモデルを作成。
カラムはuser_idとpost_id。

$ rails g model Favorite user_id:integer post_id:integer

$ rails db:migrate

ルートへのルーティングを投稿一覧ページにしgetメソッドで/postsにアクセスするボタンを全てroot_pathに変更する。

config/routes.rb
  ...(追加)
  root 'posts#index'

ログインしていない場合とログインページへ遷移

posts_controller.rb
  class PostsController < ApplicationController
    before_action :set_post, only: [:show, :edit, :update, :destroy]
    before_action :authenticate_user!

モデル間の関連付け

user.rb
  ...(追加)
  has_many :posts, dependent: :destroy
  has_many :favorites, dependent: :destroy
post.rb
  ...(追加)
  belongs_to :user
  has_many :favorites, dependent: :destroy

  def favorited_by?(user) #いいねしているかどうか
    favorites.where(user_id: user.id).exists?
  end
favorite.rb
  ...(追加)
  belongs_to :user
  belongs_to :post

投稿時にuser_idカラムにcurrent_user.idを指定

posts_controller.rb
  def create
    @post = Post.new(post_params)
    @post.user_id = current_user.id
    ...(省略)
  end

投稿一覧ページの投稿のテーブルにいいねボタンを配置(Font Awesomeを使用)

index.html.erb
  <% if post.favorited_by?(current_user) %>
    <td>
      <%= link_to fav_posts_path(post), class: "fav", remote: true do %>
        <i class="fa fa-heart" id="<%= post.id %>"></i>
      <% end %>
    </td>
  <% else %>
    <td>
      <%= link_to fav_posts_path(post), class: "fav", remote: true do %>
        <i class="fa fa-heart-o" id="<%= post.id %>"></i>
      <% end %>
    </td>
  <% end %>

いいねのためのルーティングとアクション追加

config/routes.rb
  ...(追加)
  get 'posts/fav/:id' => 'posts#fav', as: "fav_posts"

Favoritesコントローラにcreateアクションとdestroyアクションを定義しても良いが今回はPostsコントローラにいいねのためのアクションを一つ定義する。

posts_controller.rb
  ...(追加)
  def fav
    post = Post.find(params[:id])
    if post.favorited_by?(current_user)
      fav = current_user.favorites.find_by(post_id: post.id)
      fav.destroy
      render json: post.id
    else
      fav = current_user.favorites.new(post_id: post.id)
      fav.save
      render json: post.id
    end
  end

いいねしている時としていない時のボタンの色を変える

application.css
  ...(追加)
  .fa-heart {
    color: red;
  }

この時点でいいねボタンをクリックすると非同期でいいね、いいねを外す機能が実装できており、
ブラウザをリロードすると色が反映される。
リロードせずに色を反映させたいためapplication.jsにAjax通信が成功した時のイベントを書く。

application.js
  ...(省略)
  $(function() {
    $(document).on("ajax:success", ".fav", function(e) {
      if ($('#' + e.detail[0]).hasClass('fa-heart')) {
        $('#' + e.detail[0]).removeClass('fa-heart').addClass('fa-heart-o');
      } else {
    $('#' + e.detail[0]).removeClass('fa-heart-o').addClass('fa-heart');
      }
    })
  })

サーバーからレスポンスとして返ってくる情報は投稿のidのみ。
ハートアイコン(iタグ)に投稿のidを指定していてその要素がfa-heartクラスを持っていたらfa-heartクラスを消しfa-heart-oクラスをつける。
逆にfa-heart-oクラスを持っていたらfa-heart-oクラスを消しfa-heartクラスをつける。

これで投稿へのいいね機能を非同期で実装できる。

最後に

いいねの数はcountを使えば数を取得できる。
link_toにremote: trueを指定しないと非同期通信はできない。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした