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

Rails x Ajaxで「お気に入り」をオンオフできるリストを作成する

More than 1 year has passed since last update.

はじめに

  • こんな感じのAjaxで「お気に入り」のオンオフできる(ついでにカウントも)リストを作ります。
  • Deviseを使ってログインしている状態を想定しているので、Deviseをある程度知っている人向けです。

今回使うライブラリ

必要な予備知識

  1. RailsのAjax通信。
  2. BootStrap4(ビューの整形に使っているだけなので、詳しくなくていい)
  3. Slimテンプレート(2と同様)
  4. 使われているライブラリの知識

環境

$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

$ rails -v
Rails 5.2.1

実装

モデル

  • 今回はUserDogLikeモデルを使います。
  • UserモデルはDeviseで作成します。
  • Likeを中間テーブルとして、UserDogを多対多の関連づけすることで、いいねリレーションを構築します。
  • seed-fuとかを使って、前もってページネーション用のインスタンスをDBに作っておきましょう。
# マイグレーション
class CreateDogs < ActiveRecord::Migration[5.2]
  def change
    create_table :dogs do |t|
      t.string :name, null: false, default: ''
      t.integer :receive_likes_count, null: false, default: 0
    end
  end
end

class CreateLikes < ActiveRecord::Migration[5.2]
  def change
    create_table :likes do |t|
      t.references :user
      t.references :dog
    end
  end
end
class User < ApplicationRecord
  has_many :likes, dependent: :destroy
end

class Dog < ApplicationRecord
  has_many :receive_likes, dependent: :destroy, class_name: 'Like'

  # 特定のユーザーからお気に入りされているか
  def liked_by?(user)
    user.likes.exists?(dog_id: id)
  end

  # Ajax実行時に、DOMエレメントを特定するID
  def id_attribute
    "dog-#{id}"
  end
end

class Like < ApplicationRecord
  belongs_to :user
  belongs_to :dog
  # counter culture設定
  counter_culture :dog, column_name: 'receive_likes_count'
end

コントローラー

  • 今回のページをsample#indexアクションとします。
  • 「いいね」しても順番が表示の順番が変わらないよう、orderを設定しておきます。
class SampleController < ApplicationController
  def index
    @dogs = Dog.all.order(created_at: :asc) 
  end
end
  • また、いいねをAjaxでオンオフするためのAPIコントローラーapi/like_controller.rbも追加します。
  • このコントローラー呼び出し時に実行されるJSファイルは後ほど記載します。
class Api::LikeController < ApplicationController
  before_action :setup_dog!
  before_action :setup_like!

  def create
    @like.save
  end

  def destroy
    @like.destroy
  end

  private

  def setup_dog!
    @dog = Dog.find(params[:dog_id])
  end

  def setup_like!
    # create時はまだ存在していないのでinitializeされ、
    # destroy時は既に存在しているのでfindされる。
    @like = current_user
              .likes
              .find_or_initialize_by(dog_id: @dog.id)
  end
end
  • ルーティングはこんな感じ。
namespace :api do
  post 'like/:dog_id' => 'like#create', as: :like
  delete 'like/:dog_id' => 'like#destroy', as: :unlike
end

ヘルパー

  • いいねをオンオフするボタンをレンダーするヘルパーメソッドを作成する。
  • これにより、ログイン中のユーザーが特定のDogモデルをお気に入りしたかどうかを判定して、アイコンとリンクを出し分けることができる。
module MyHelper
  def toggle_like_button(dog)
    # ログインしていない場合
    return fa_icon 'heart-o', text: dog.receive_likes_count if current_user.blank?

    liked = dog.liked_by?(current_user)
    icon = fa_icon (liked ? 'heart' : 'heart-o'), text: dog.reload.receive_likes_count
    if liked
      # お気に入りを外すボタン
      link_to icon, api_unlike_path(dog_id: dog.id), remote: true, method: :delete
    else
      # お気に入りするボタン
      link_to icon, api_like_path(dog_id: dog.id), remote: true, method: :post
    end
  end
end

ビュー

  • 今回用意するビューは、大元のページ用と、Dogのパーシャルビューの2つです。
# ページ
app/views/sample/
├── _dog.html.slim
└── index.html.slim
/ index.html.slim
.container.my-5
  .row
    .col-md-6.offset-md-3
      #project-list(style='overflow-y: scroll; height: 80vh;')
        = render @dogs
/ _dog.html.slim
.card.card-body.mb-3(id="#{dog.id_attribute}")
  / この.like要素をAjaxで書き換える
  .like
    = toggle_like_button(dog)
  h3.card-title = dog.name

JSファイル

  • 最後にlikeコントローラー呼び出し時に呼び出されるJSファイルを追加します。
app/views/api/like/
├── create.js.erb
└── destroy.js.erb
// create.js.erb

// toastrの通知処理
toastr.success("<%= "#{@like.dog.name}をお気に入りに追加しました" %>")
// 特定のLIKEボタンのDOMを入れ替える。
$("<%= "##{@dog.id_attribute} .like" %>").html("<%= j toggle_like_badge(@dog) %>") 
// destroy.js.erb

toastr.error("<%= "#{@dog.name}をお気に入りから削除しました" %>")
$("<%= "##{@dog.id_attribute} .like" %>").html("<%= j toggle_like_badge(@dog) %>")

おわりに

  • かけあしでしたが、ひとまずこれで「お気に入り処理」ができるようになりました。
  • いろんな便利なライブラリがあるっていいですね。
Coolucky
admin-guild
「Webサービスの運営に必要なあらゆる知見」を共有できる場として作られた、運営者のためのコミュニティです。
https://admin-guild.slack.com
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
ユーザーは見つかりませんでした