1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】イイネの非同期通信(Ajax)を分かりやすく解説する。

Posted at

#まずはじめに

ポートフォリオに非同期通信を実装したので
アウトプットも兼ねて非同期通信の解説をしていきます。

なるべく分かりやすく解説をしていくので、
参考にしていただけたら嬉しいです!

##非同期通信とは。

非同期通信とはなんぞやといいますと、

Ajax(Asynchronous JavaScript + XML)は、ページ遷移せずにページを読み込める仕組みです。これを非同期通信と言います。

これなんですね。

つまり、ページの更新を行わなくてもページが読み込めるので
いちいちページが止まったりせずにストレスなく使えるんですね!

##非同期通信の流れ

非同期通信の流れを説明する前に通常の処理を簡単に解説します。

  1. HTTPリクエストがルーティングに飛ぶ
  2. ルーティングがどのコントローラーのアクションが判別
  3. コントローラーがどのモデルに対して指示を出す
  4. モデルが必要なデータをコントローラーに戻す
  5. コントローラーがviewファイルにモデルから持ってきたデータを送る

これが大まかな通常のやりとりの流れになります。

一方で非同期通信は最後の部分が変わってきます。

  1. HTTPリクエストがルーティングに飛ぶ
  2. ルーティングがどのコントローラーのアクションが判別
  3. コントローラーがどのモデルに対して指示を出す
  4. モデルが必要なデータをコントローラーに戻す
  5. アクション名.js.erbのファイルを探して非同期を行う

通常であればviewファイルを読み込むのですが
非同期通信となるとjs.erbファイルを読み込むため
ページ全体を更新する必要はなく一部がjs.erbに書き換わります。

これがイイネの非同期通信の流れになります!

##実装

それでは実際にどんな風に実装をするか見ていきましょう。

一応イイネ機能の部分から説明をしますが、説明不要の方は
viewファイルへ飛んでくださいね!

結構簡単に実装できますよ。
必要になるのはこちら

  1. マイグレーションファイル
  2. ルーティングファイル
  3. モデルファイル
  4. viewファイル
  5. js.erbファイル
  6. コントローラー

これらの中身を書いていきます。

##ステップ1:マイグレーションファイル

まずは、マイグレーションファイルから見ていきましょう。

php/config/db/migrate/...rb
class CreateLikes < ActiveRecord::Migration[5.2]
  def change
    create_table :likes do |t|
      t.integer :user_id, null: false
      t.integer :post_id, null: false
      t.timestamps
    end
  end
end

いいねする際はイイネした投稿のidとuser_idが必要になるので、
2つカラムを用意してあげましょう!

書いたら、rails db migrateでテーブルを作成します。

##ステップ2:ルーティングファイル

いいね機能については、投稿に紐づいたイイネとなるので
投稿機能にネストをさせる必要がでてきます。

php/config/db/migrate/...rb
Rails.application.routes.draw do

 # 投稿機能にネストさせる形でいいね機能を設定
    resources :posts do
      resource :likes, only: [:create, :destroy]
    end

end

このように書くことでurlのpostの中にlikeを含ませることができます。

実際のURLはこんな感じ。

image.png

ちなみにresourceとlikesが複数形にならないのはidが不要だからです。
いいね自体のidがなくてもuserとpostのidの二つがあればどの投稿に誰かイイネしたか
特定ができるためこのような書き方をしています。
resourceと書くことでurlにはイイネのidは含まれなくなります。

##ステップ3:モデルファイル

php/app/models/post.rb
 
 # いいね判定メソッド
  def liked_by?(user)
    likes.where(user_id: user.id).exists?
  end

この記述をモデルにしましょう。
これは後ほどviewファイルで必要になるのですが
投稿にいいねがついているかついていないかを識別(判定)するために必要です。

##ステップ4:viewファイル

次にviewファイルを見ていきます。

今回は投稿のeach文で表示している記事一つ一つに
いいね機能を実装しています。

image.png

それではコードを見ていきましょう!

php/app/views/posts/index.html.erb

<h2><i>Timeline</i></h2>
 <% @posts.each do |post| %>
  <div class="far fa-thumbs-up">
    <span id="likes_<%= post.id %>"><%= render partial: 'likes/like', locals: {post: post}%></span>
  likes
  </div>
 <% end %>

今回はeachで回しているため、どの投稿のidか?というのを識別するためにpost.idという書き方をして
どの投稿のidにイイネをつけるのかイイネを外すのかをjsファイルが判別するために必要になります。

後ろのrenderは部分テンプレートとなりますので下に記述します。

php/app/views/likes/_like.html.erb

<% if post.liked_by?(current_user) %>
    <%= link_to post_likes_path(post), method: :delete, remote:true do %>
        <%= post.likes.count %>
    <% end %>
    <% else %>
    <%= link_to post_likes_path(post), method: :post, remote:true do %>
        <%= post.likes.count %>
    <% end %>
<% end %>

ここで<% if post.liked_by?(current_user) %>に書かれているのは
postモデルに記述をしたメソッドになります。

部分テンプレートのlink_toに注目してほしいのですがこのリンクがcreateとdestroyへの
リンクとなっているため、このリンクを押すとlikeコントローラーへと飛んでいきます。

postを書いているのはコントローラーの部分と繋がるのですが
postの情報をlikeコントローラーに保存をしないといけないのでlink_toで
postをブロック変数として格納して、likeコントローラーへ飛ばします。

非同期通信を行う際は、remote:trueという記載が必要になります。
これをすることで非同期通信ができるようになるわけです。

##ステップ5:js.ファイル

php/app/views/likes/create.js.erb

<!--@postが実際に動いている内容としてはlikes_controllerが発火しているので@postが必要となる-->
$('#likes_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");


@postで使用しているインスタンス変数は、イイネ機能のコントローラーからの
インスタンス変数になるため@postとインスタンス変数にする必要があります。

前の$の部分がviewファイルを特定しているcssでいうセレクタのような役割で
その後ろが非同期通信の役割を果たす記述となります。

##ステップ6:コントローラー

それでは最後にいいね機能のコントローラーをみていきましょう。

php/app/controllers/likes_controller.rb

class LikesController < ApplicationController
  before_action :authenticate_user!

  def create
    @post = Post.find(params[:post_id])
    like = current_user.likes.new(post_id: @post.id)
    like.save
    # 通知の作成
    # create_nitifiaction_likeはpostモデルを参照
    @post.create_notification_like(current_user)
  end

  def destroy
    @post = Post.find(params[:post_id])
    like = current_user.likes.find_by(post_id: @post.id)
    like.destroy
  end
end

createとdestroyはほぼ同じ要領になるため、createのみを説明します。

最初にlikeテーブルにはpost_idとuser_idが必要になるため
それらに必要なデータを格納するための記述を書いていきます。

@postでやっていることとしては、viewファイルから飛んできた
postの情報があるのでPost.findとすることでpostの情報一式を持ってこれます。

そして次でやっていることとしてはuser_idとpost_idを格納する準備をします。

current_user.likesという記述をすることで現在のユーザーがイイネした
すべての情報を持ってくることができます。

そのあとにnewでpost_id: @post.idとすることで、post_idに格納する準備が整います。
ちなみにですがnewの後にuser_idは書かなくていいの?ということになりますが
Railsは非常に頭がいいため、likeモデルに必要なuser_idが必要であると判断してくれ
current_user.Likesとすることでその中にあるuser_idを格納してくれるんです。

頭いいですよね。

ということでこれでLikeテーブルに保存が完了しました!

destroyもほぼ同じような内容になりますがfind_byをしているところに注目。
find_byメソッドは、テーブルにある特定のカラムのみを引っ張りたい時に使用します!

なのでfind_by(post_user: @post.id)

という書き方をすることで投稿のidをもってこれるわけですね!

##まとめ

いかがでしたでしょうか?

非同期についてはjsファイルとviewにremote trueを記述することで
簡単に実装することができます。

いいね機能についても少し内容が難しいと思うので
何回も読んでみて理解を深めてくださいね!

わかりづらい箇所や不明点があれば気軽にコメントください!

それでは、チャオ!

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?