LoginSignup
3
2

More than 1 year has passed since last update.

[Ajax]フォロー機能を非同期にした

Posted at

はじめに

本記事では、フォロー機能を非同期通信にした方法を記述します。

以前にいいね機能について、非同期通信にした記事も投稿しておりますので、
ご参照ください。

前提

フォロー機能が同期通信できているものとします。

コード

早速ですが、該当箇所のコードを記載します。

コントローラー

showページにフォローボタンやフォロワーを確認できるボタンがあります。
後述のモデルに合わせてfollowingsや`followersメソッドを作成します。

また、usersfollowersなどに分けている方もいらっしゃいましたが、
私は、followingsや`followersに分けた方がわかりやすいと思ったため、こうしています。

users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
    @foods = @user.foods.order("created_at DESC")
  end

  def followings
    user = User.find(params[:id])
    @users = user.followings
  end

  def followers
    user = User.find(params[:id])
    @users = user.followers
  end

省略

end

relationshipsテーブルは、userとuserの関係を示す、中間テーブルになります。
また、その関係を作る(create)、壊す(destroy)の2つが必要になります。

relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :authenticate_user!

  def create
    @user = User.find(params[:user_id])
    following = Relationship.create(follower_id: params[:user_id], following_id: current_user.id)
  end

  def destroy
    @user = User.find(params[:user_id])
    following = Relationship.find_by(follower_id: params[:user_id], following_id: current_user.id)
    following.destroy
  end
end

モデル

同期通信と特に変わりはありません。

user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :foods, dependent: :destroy
  has_many :likes, dependent: :destroy
  has_many :liked_foods, through: :likes, source: :food

  has_many :relationships, foreign_key: :following_id
  has_many :followings, through: :relationships, source: :follower
  has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: :follower_id
  has_many :followers, through: :reverse_of_relationships, source: :following

  has_one_attached :icon

  has_many :comments

  def already_liked?(food)
    self.likes.exists?(food_id: food)
  end

  def is_followed_by?(user)
    reverse_of_relationships.find_by(following_id: user.id).present?
  end


end

ここも同期通信と特に変わりはありません。

relationship.rb
class Relationship < ApplicationRecord
  belongs_to :following ,class_name: "User"
  belongs_to :follower, class_name: "User"
end

ビュー

show.html.erb
記述が長いので、部分テンプレートです。
ちなみに、クラス名を
class="mypage-follow-contents-<%= @user.id %>"
にしないと、誰をフォローするのかわからないままですので、
エラーが出続けることになります。

<div class="mypage-intro-topright">
  <div class="mypage-follow-contents-<%= @user.id %>">
    <%= render partial:"relationships/relationship", lacals: {user: @user} %>
  </div>
</div>

followers.html.erbフォロワーの一覧を確認できるページ。ここはあまり関係ないと思います。

<div class="followers-contents">
  <div class="row d-flex justify-content-center">
    <div class="col-10 mt-5">
      <h1>FOLLOWERS</h1>
        <h2 class="followers-contents-title">Nickname</h2>
          <% @users.each do |user| %>
            <%= link_to user_path(user) do %>
              <%= user.nickname %>
            <% end %>
              <% if user != current_user %>
                <% if user.is_followed_by?(current_user) %>
                  <%=link_to user_relationships_path(user), method: :delete do %>
                    <button type="button" class="btn btn btn-danger">フォロー解除</button>
                  <% end %>
                <% else %>
                  <%=link_to user_relationships_path(user), method: :post do %>
                    <button type="button" class="btn btn btn-primary">フォロー</button>
                  <% end %>
                <% end %>
              <% end %>
          <% end %>
    </div>
  </div>
</div>

followings.html.erbフォローしている人を確認できるページ。ここはあまり関係ないと思います。

<div class="followings-contents">
  <div class="row d-flex justify-content-center">
    <div class="col-10 mt-5">
      <h1>FOLLOWING</h1>
        <h2 class="followings-contents-title">Nickname</h2>
          <% @users.each do |user| %>
            <%= link_to user_path(user) do %>
              <%= user.nickname %>
            <% end %>
              <% if user != current_user %>
                <% if user.is_followed_by?(current_user) %>
                  <%=link_to user_relationships_path(user), method: :delete do %>
                    <button type="button" class="btn btn btn-danger">フォロー解除</button>
                  <% end %>
                <% else %>
                  <%=link_to user_relationships_path(user), method: :post do %>
                    <button type="button" class="btn btn btn-primary">フォロー</button>
                  <% end %>
                <% end %>
              <% end %>
          <% end %>
    </div>
  </div>
</div>

_relationship.html.erb
show.html.erbにあった部分テンプレートです。

remote: trueこれが特に大事です。
というか非同期通信のスタートがこれなんではないかというぐらい重要です。
これにより、非同期通信をOKするものになり、リクエストがjs形式になります。

そのため、後ほど出てきますが、
ファイルはjs.erbファイルということになります。

<div class="mypage-followcount-btn">
  <%= link_to followings_user_path(@user) do %>
    <button class="btn btn-mypage-follow">フォロー
      <i class="fas fa-angle-right fa-position-right">
        <%= @user.followings.count %>
      </i>
    </button>
  <% end %>
  <%= link_to followers_user_path(@user) do %>
    <button class="btn btn-mypage-follow">フォロワー
      <i class="fas fa-angle-right fa-position-right">
        <%= @user.followers.count %>
      </i>
    </button>
  <% end %>
  <% if user_signed_in? && @user == current_user %>
    <div class="mypage-introduction-edit">
      <%= link_to edit_user_path(current_user) do %>
        <button class="btn btn-mypage-introduction-edit">プロフィール編集
          <i class="fas fa-user-edit"></i>
        </button>
      <% end %>
    </div>
  <% else %>
    <%# <% unless @user == current_user %>
    <div class="mypage-follow-btn">
      <% if @user.is_followed_by?(current_user) %>
        <%= link_to user_relationships_path(@user), method: :delete, remote: true do %>
          <button class="btn btn-follow-delete">フォロー中</button>
        <% end %>
      <% else %>
        <%= link_to user_relationships_path(@user), method: :post, remote: true do %>
          <button class="btn btn-follow-create">フォローする</button>
        <% end %>
      <% end %>
    </div>
  <% end %>
</div>

create.js.erbフォロー
jについては、以下にも記載したので省略します。

$('.mypage-follow-contents-<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: {user: @user}) %>")

部分テンプレートにより、
前述の以下の部分に進みます。

<% if @user.is_followed_by?(current_user) %>
  <%= link_to user_relationships_path(@user), method: :delete, remote: true do %>
    <button class="btn btn-follow-delete">フォロー中</button>
  <% end %>
<% else %>
   <%= link_to user_relationships_path(@user), method: :post, remote: true do %>
    <button class="btn btn-follow-create">フォローする</button>
   <% end %>
<% end %>

destroy.js.erbフォロー解除
create.js.erbと同じです。

$('.mypage-follow-contents-<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: {user: @user}) %>")

以上です。
いかがでしょうか。

終わりに

非同期通信、終わってみれば「簡単」だと思いますが、
できるまでは難しかったなというのが、印象でした。

アプリケーションにおいて非同期化してしまった方が良いものは全て非同期化した方が良さそうですね。

以下、参考サイトです。
[Rails]Ajaxを用いて非同期でフォロー機能の実装
【Rails】Ajaxを用いた非同期フォロー機能の実装
Railsで実装したフォロー機能の非同期化がうまくいかない。
Ruby on Rails チュートリアル

明日も頑張ります!!

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