はじめに
本記事では、フォロー機能を非同期通信にした方法を記述します。
以前にいいね機能について、非同期通信にした記事も投稿しておりますので、
ご参照ください。
前提
フォロー機能が同期通信できているものとします。
コード
早速ですが、該当箇所のコードを記載します。
コントローラー
showページにフォローボタンやフォロワーを確認できるボタンがあります。
後述のモデルに合わせてfollowings
や``followers`メソッドを作成します。
また、users
とfollowers
などに分けている方もいらっしゃいましたが、
私は、followings
や``followers`に分けた方がわかりやすいと思ったため、こうしています。
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つが必要になります。
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
モデル
同期通信と特に変わりはありません。
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
ここも同期通信と特に変わりはありません。
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 チュートリアル
明日も頑張ります!!