LoginSignup
2
2

More than 1 year has passed since last update.

[Rails] Ajaxで作るクリップ機能(いいね機能)

Last updated at Posted at 2021-10-10

ポートフォリにAjaxでいいね機能を実装した時のメモです。

Ajaxとは?

JavaScriptを使ってサーバーにリクエストを投げて、レスポンスに基づきHTMLに変更を加える技術のことらしい。
分かりやすくいうとページのリロードなしで表示が切り替わるってこと。
身近なのはやっぱりTwitterのいいねですかね。

多対多のクリップ機能は既に実装済みでとします。
ちなみに今回の場合は、以下のようにUserモデルとEventモデルの中間テーブルとしてClipモデルがあることとしてます。

スクリーンショット 2021-10-08 16.27.20.png

完成イメージ

クリップしたときにリロードなしでクリップできていてクリップしたイベントとしてクリップイベント一覧に反映されている。
ezgif.com-gif-maker (5).gif

実装

① jQuery導入

jQueryを導入していきます。

ターミナル
yarn add jQuery

yarnとはJavaScriptのライブラリを管理するもの。Rubyでいうgemのようなもの。
npmというのもあるがRailsでは基本的にyarnを使うらしい。

次にwebpackerのサーバーを立ち上げます。

ターミナル
bin/webpack-dev-server

webpackerとはwebpackのRails版みたいなもので複数のJavaScriptファイルを読み込んで1つのファイルに出力してくれます。
これをすることでJavaScriptの読み込みを早くすることができ開発スピードを上げることができます。

それでは動作確認をしていきます。

app/javascript/packs/application.js
import $ from 'jquery'

document.addEventListener('DOMContentLoaded', () => {
  $('.title').on('click', () => {
    window.alert('CLICKED')
  })
}) 

確認方法は何でもいいんですが、今回はtitleというクラスのついた部分をクリックしたらアラートが出るようにして動作を確認していきます。
以下のようにタイトルをクリックしてアラートが出てればOKです。

② クリップの状態を非同期で取得する

まずはアクセスした時にイベントがクリップしてるのかしてないかの状態を判断するAPIを作っていきます。

クリップしているかどうかのメソッドを作って、

app/models/user.rb
def has_clipped?(event)
  clips.exists?(event_id: event.id)
end

現状のclip_controller.rbにshow actionを追加します。

config/routes.rb
resource :clip, only: [:show, :create, :destroy]
app/controllers/clips_controller.rb
class ClipsController < ApplicationController
  before_action :authenticate_user!

#-------------------------追加-----------------------
  def show
    event = Event.find(params[:event_id])
    clip_status = current_user.has_clipped?(event)
    render json: { hasClipped: clip_status }
  end
#---------------------------------------------------

  def create
    event = Event.find(params[:event_id])
    event.clips.create!(user_id: current_user.id)
    redirect_to event_path(event)
  end
  def destroy
    event = Event.find(params[:event_id])
    clip = event.clips.find_by!(user_id: current_user.id)
    clip.destroy!
    redirect_to event_path(event)
  end
end

やっていることはアクセスしたイベントがクリップされているかいないかを判断しています。
クリップしているイベントには{"hasCliped":true}というデータがjsonで返されます。
このステータス状態によってHTMLを変更していきます。

axiosをインストールします。

ターミナル
yarn add axios

axiosでgetリクエストをしてステータス状態を取得します。

その前に、イベントのステータス状態を取得するのにはevent_idが必要なのでクリップボタンを設置するHTML部分に以下を記述することでevent_idの取得ができます。

app/views/events/show.html.haml
.container#event-show{data: {event_id: @event.id}}

あとは取得したevent_idを使ってgetリクエストしていきます。

app/javascript/packs/application.js
import axios from 'axios'

document.addEventListener('DOMContentLoaded', () => {
  const dataset = $('#event-show').data()
  const eventId = dataset.eventId
  axios.get(`/events/${eventId}/clip`)
    .then((response) => {
      console.log(response)
      const hasClipped = response.data.hasClipped
    })
}) 

axiosのレスポンスの結果次第でクリップボタンの表示をJavaScriptで切り替えできるようにしていきます。

app/views/events/show.html.haml
.clip
  - if user_signed_in?
    .clip__active.hidden.active-clip クリップ中
    .clip__icon.hidden.inactive-clip クリップする
app/assets/stylesheets/application.scss
.hidden {
  display: none;
}

クリップボタンの部分のHTMLにhiddenクラスを両方につけdisplay: none;のcssを当てます。

app/javascript/packs/application.js
const handleClipDisplay = (hasClipped) => {
  if (hasClipped) {
    $('.active-clip').removeClass('hidden')
  } else {
    $('.inactive-clip').removeClass('hidden')
  }
}

axiosのレスポンスのステータス状態に合わせてhiddenクラスをつけるか外すかを決めることでクリップボタンのHTMLが変化します。
そしてこのfunctionを先程のgetリクエスト部分に記述します。

app/javascript/packs/application.js
import axios from 'axios'

document.addEventListener('DOMContentLoaded', () => {
  const dataset = $('#event-show').data()
  const eventId = dataset.eventId
  axios.get(`/events/${eventId}/clip`)
    .then((response) => {
      console.log(response)
      const hasClipped = response.data.hasClipped
     //-------追加-------------------
      handleClipDisplay(hasClipped)
    //------------------------------
    })
}) 

これで非同期でクリップしているかしていないかを取得することができます。

③非同期でクリップする

次は実際にクリップするpostリクエスト部分とクリップを外すdeleteリクエスト部分を作っていきます。

APIを作っていきます。
リダイレクトしていた部分をrenderできるように変更していきます。

app/controllers/clips_controller.rb
class ClipsController < ApplicationController
  before_action :authenticate_user!

  def show
    event = Event.find(params[:event_id])
    clip_status = current_user.has_clipped?(event)
    render json: { hasClipped: clip_status }
  end

  def create
    event = Event.find(params[:event_id])
    event.clips.create!(user_id: current_user.id)
     render json: { status: 'ok' } #変更部分
  end
  def destroy
    event = Event.find(params[:event_id])
    clip = event.clips.find_by!(user_id: current_user.id)
    clip.destroy!
     render json: { status: 'ok' } #変更部分
  end
end

axiosでpostリクエストを作る前にターミナルで以下をインストールしてapplication.jsに記述します。

ターミナル
yarn add rails-ujs
app/javascript/packs/application.js
import { csrfToken } from 'rails-ujs'

axios.defaults.headers.common['X-CSRF-Token'] = csrfToken()

ここでやっているのはセキュリティ対策です。
postリクエストの場合はgetリクエストと違いセキュリィティ的に問題があるので上の記述がないとpostリクエストが通りません。
csrfTokenというのは鍵みたいなもので、その鍵をaxiosのときはデフォルトで持たせるように記述しています。

postリクエスト部分

app/javascript/packs/application.js
$('.inactive-clip').on('click', () => {
  axios.post(`/events/${eventId}/clip`)
    .then((response) => {
      if (response.data.status === 'ok') {
        $('.active-clip').removeClass('hidden')
        $('.inactive-clip').addClass('hidden')
     }
     console.log(response)
    })
    .catch((e) => {
      window.alert('Error')
      console.log(e)
    })
})

.then((response) => {}はリクエストがうまくいった時の処理。今回はリクエストがうまくいったらクリップした状態にボタンを変更させています。
.catch((e) => {}の部分はリクエストが失敗した時の処理。今回はwindow.alertでErrorという文字列を表示させます。

最後にクリップを外すdeleteリクエスト

$('.active-clip').on('click', () => {
  axios.delete(`/events/${eventId}/clip`)
    .then((response) => {
      if (response.data.status === 'ok') {
        $('.active-clip').addClass('hidden')
        $('.inactive-clip').removeClass('hidden')
      }
       console.log(response)
     })
    .catch((e) => {
      window.alert('Error')
      console.log(e)
    })
})

やっているのはpostの逆の処理です。

動作確認をして画面がリロードせずクリップできていたら完成です!

2
2
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
2
2