質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.09%

【Rails】リロードしないとフォローボタンが切り替わらない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 245

punchan36

score 30

前提・実現したいこと

Ruby on Railsでユーザー同士のフォロー機能を作っています。
こちらのサイト等を参照しました。
Ruby on Rails ~フォロー(友達申請)機能の実装(コードメモ)

上手く機能しているのですが、フォロー・アンフォローボタンがリロードをしないと切り替わりません。
「remote: true」を適用しており、フォローしているユーザー数は瞬時に切り替わってくれます。ボタンも、フォローしているならアンフォローボタンを、フォローしていないならフォローボタンに瞬時に切り替わって欲しいです。
どなたかお知恵を拝借頂けると幸いです。

※数分前に同じ質問を投稿しましたが、書いた文章がほぼ消えた状態で送信されてしまいました。申し訳ありません。

該当のソースコード(show.html.erb)

<div class="main user-show">
  <div class="container">
    <div class="user">
      <img class="cover_photo" src="<%= "/user_cover_images/#{@user.cover_image_name}" %>">
      <img class="photo" src="<%= "/user_images/#{@user.image_name}" %>">
    </div>

    <div class="tab-container">
      <!--タブ-->
      <ul class="tab-group">
          <li class="tab is-active">Bio</li>
          <li class="tab"><%= (@user.name + "'s organized events") %></li>
          <li class="tab">Schedule</li>
      </ul>

      <!--タブを切り替えて表示するコンテンツ-->
      <div class="panel-group">
          <div class="panel is-show">Content-A</div>

          <div class="panel">
          <% @user.posts.each do |post| %>
            <div class="posts-index-item">
              <div class="post-left">
                <img src="<%= "/user_images/#{post.user.image_name}" %>">
              </div>
              <div class="post-right">
                <div class="post-user-name">
                  <%= link_to(post.user.name, "/users/#{post.user.id}") %>
                </div>
                <%= link_to(post.title, "/posts/#{post.id}") %>
              </div>
            </div>
          <% end %>
        </div>

          <div class="panel">
          <% @likes.each do |like| %>
            <% post = Post.find_by(id: like.post_id) %>
            <div class="posts-index-item">
              <div class="post-left">
                <img src="<%= "/user_images/#{post.user.image_name}" %>">
              </div>
              <div class="post-right">
                <div class="post-user-name">
                  <%= link_to(post.user.name, "/users/#{post.user.id}") %>
                </div>
                <%= link_to(post.title, "/posts/#{post.id}") %>
              </div>
            </div>
          <% end %>
        </div>
      </div>
    </div>

    <% if @user.id == @current_user.id %>

    <% else %>
      <% if @isRoom == true %>
        <p><a href="/rooms/<%= @roomId %>">チャットへ</a>
      <% else %>
        <%= form_for @room do |f| %>
          <%= fields_for @entry do |e| %>
            <%= e.hidden_field :user_id, :value=> @user.id %>
          <% end %>
          <%= f.submit "チャットを始める" %>
        <% end %>
      <% end %>
    <% end %>

    <h2><%= @user.name %></h2>
    <% if @user.id == @current_user.id %>
      <%= link_to("編集", "/users/#{@user.id}/edit") %>
      <%= link_to("削除", "/users/#{@user.id}/destroy", {method: :post, data: {confirm: "Are you sure to delete your account?"}, class: "link"}) %>
    <% end %>

    <div id="follow-btn">
      <%= render 'users/follow_form' %>
    </div>
    <h4>
      <%= render 'users/stats' %>
    </h4>

 </div>
</div>

該当のソースコード(_follow_form.html.erb)

<% unless @current_user.id == @user.id %>
  <% if @current_user.following?(@user) %>
    <%= render 'users/unfollow', user: @user %>
  <% else %>
    <%= render 'users/follow', user: @user %>
  <% end %>
<% end %>

該当のソースコード(_follow.html.erb)

<%= form_for(@current_user.following_relationships.build(following_id: @user.id),
             remote: true) do |f| %>
  <div><%= f.hidden_field :following_id %></div>
  <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>

該当のソースコード(_unfollow.html.erb)

<%= form_for(@current_user.following_relationships.find_by(following_id: @user.id),
             html: { method: :delete },
             remote: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>

該当のソースコード(relationships.controller.rb)

class RelationshipsController < ApplicationController
  def create
    @user = User.find(params[:relationship][:following_id])
    @current_user.follow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

  def destroy
    @user = Relationship.find(params[:id]).following
    @current_user.unfollow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end
end

該当のソースコード(users_controller.rb)

class UsersController < ApplicationController
  before_action :authenticate_user, {only: [:index, :show, :edit, :update]}
  before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]}
  before_action :ensure_correct_user, {only: [:edit, :update]}

  def index
    @users = User.all.order(created_at: :desc)
  end

  def show
    @user = User.find_by(id: params[:id])
    @likes = Like.where(user_id: @user.id)
    @currentUserEntry=Entry.where(user_id: @current_user.id)
    @userEntry=Entry.where(user_id: @user.id)
    if @user.id == @current_user.id
    else
      @currentUserEntry.each do |cu|
        @userEntry.each do |u|
          if cu.room_id == u.room_id then
            @isRoom = true
            @roomId = cu.room_id
          end
        end
      end
      if @isRoom
      else
        @room = Room.new
        @entry = Entry.new
      end
    end
  end

  def follow
    @user = User.find_by(id: params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(
      name: params[:name],
      email: params[:email],
      image_name: "default_user.jpg",
      cover_image_name: "default_cover_user.jpg",
      password: params[:password]
    )
    if @user.save
      session[:user_id] = @user.id
      flash[:notice] = "ユーザー登録が完了しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/new")
    end
  end

  def edit
    @user = User.find_by(id: params[:id])
  end

  def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    @user.email = params[:email]
    if params[:image]
      @user.image_name = "#{@user.id}.jpg"
      image = params[:image]
      File.binwrite("public/user_images/#{@user.image_name}", image.read)
    end
    if params[:cover_image]
      @user.cover_image_name = "#{@user.id}_cover.jpg"
      cover_image = params[:cover_image]
      File.binwrite("public/user_cover_images/#{@user.cover_image_name}", cover_image.read)
    end
    if @user.save
      flash[:notice] = "ユーザー情報を編集しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/edit")
    end
  end

  def destroy
    @user = User.find_by(id: params[:id])
    @user.destroy
    flash[:notice] = "アカウントを削除しました"
    redirect_to("/posts/index")
  end

  def login_form
  end

  def login
    @user = User.find_by(email: params[:email])
    if @user && @user.authenticate(params[:password])
      session[:user_id] = @user.id
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      @error_message = "メールアドレスまたはパスワードが間違っています"
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
  end

  def logout
    session[:user_id] = nil
    flash[:notice] = "ログアウトしました"
    redirect_to("/login")
  end

  def likes
    @user = User.find_by(id: params[:id])
    @likes = Like.where(user_id: @user.id)
  end

  def ensure_correct_user
    if @current_user.id != params[:id].to_i
      flash[:notice] = "権限がありません"
      redirect_to("/posts/index")
    end
  end

  def top
    @user = User.find_by(id: params[:id])
    @likes = Like.where(user_id: @current_user.id)
  end

  def following
    @title = "フォロー"
    @user = User.find(params[:id])
    @users = @user.followings
    render 'show_follow'
  end

  def followers
    @title = "フォロワー"
    @user = User.find(params[:id])
    @users = @user.followers
    render 'show_follow'
  end

end

該当のソースコード(relationships#create.js.erb)

$("#user_<%= @user.id %>").html("<%= escape_javascript(render('users/follow_form', user: @user)) %>")
$("#followers").html('<%= @user.followers.count %>')

該当のソースコード(relationships#destroy.js.erb)

$("#user_<%= @user.id %>").html("<%= escape_javascript(render('users/follow_form', user: @user)) %>")
$("#followers").html('<%= @user.followers.count %>')

該当のソースコード(index.js.erb)

$("#follow-btn").html("<%= escape_javascript(render partial: 'users/follow_form' %>")

試したこと

最近jQueryを用いページ内タブを実装したのですが、その際にもリロードをしないとタブの内容が切り替わらない事象が発生しました。そこでは「Turbolinks」の設定を変えるなどして解決しましたが、今回のフォローボタンではjQueryも使用していない為、解決策が分からず困っています。

補足情報(FW/ツールのバージョンなど)

ruby 2.6.4p104
RubyGems 3.0.3
Rails 5.2.3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

コントローラーの中身を見ていないので推測でしかないのですが、フォローボタンorアンフォローボタンを押した時、「ユーザーをフォローする処理」と「jQueryでフォローしているユーザー数を書き換える処理」が走っているような気がします。

「フォローボタンを押した時の処理」に「jQuery側でアンフォローボタンを表示する」、「アンフォローボタンを押した時の処理」に「jQuery側でフォローボタンを表示する」コードを追加してあげれば、やりたかったことが実現できるのではないかなと思います。

remote: true を使うと、ページを更新せずリクエストだけ投げてくれるようになります。

リクエストを送ってもHTMLの中身はそのままなので、フォローボタンの表示は変わりません。リロードせずにHTMLの中身を変更したい場合は、jQuery側で書き換えてあげる必要があります。

- - - - - - ↓ 以下追記です ↓ - - - - - - -

Ajaxのリクエストについてですが、コントローラーの下記部分に記載があります。

class RelationshipsController < ApplicationController
  def create
    @user = User.find(params[:relationship][:following_id])
    @current_user.follow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js # ← ★★★ この部分 ★★★
    end
  end

Relationshipsのcreateアクションでは app/views/relationships/create.js.erb が、
Relationshipsのdestroyアクションでは app/views/relationships/destroy.js.erb が呼ばれます。

→ index.js.erbにフォローボタンの処理を書いても、それを呼び出す記述がないので動きません。

(参考サイトも拝見しましたが、そちらではコントローラーのcreateアクションとdestroyアクションの中でindex.js.erbを呼び出しているようです)

今回の場合であれば、フォローボタンの処理だけをindex.js.erbに分けて書くのではなく、「createアクションで動かす処理はcreate.js.erb」 「destroyアクションで動かす処理はdestroy.js.erb」に書いた方が分かりやすいのではないかと思います。

→ index.js.erbに書いたような処理を、すでに作成している create.js.erbdestroy.js.erbに記載すると良いのではないでしょうか…?

【 参考ページ 】 Rails で JavaScript を使用する - Railsガイド

  • 「4.1 シンプルな例」 の辺り

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/26 17:48

    ご回答有難うございます!関係がありそうなコントローラを追加表示しました。
    HTMLの中身は別で処理しないと、リロードなしでは変わらないと言う事を知りませんでした…。
    非同期処理について以下のサイト等で勉強し「js.erb」も追加してみましたが、まだリロードしないと動いてくれません。
    https://whatsupguys.net/programming-school-dive-into-code-learning-82/

    いくつかコードを追加表示しましたので、間違いがありましたらご指摘頂けると有難いです!

    キャンセル

  • 2020/02/26 19:15

    内容の追記ありがとうございます 回答の方に色々追記しました!

    キャンセル

  • 2020/02/27 20:32

    ご回答有難うございます!
    以下の1行を「create.js.erb」、「destroy.js.erb」に加える事で無事解決致しました。
    $("#follow-btn").html("<%= escape_javascript(render('users/follow_form', user: @user )) %>")

    以前ネットで参考にした「respond_to」内の記述等を、完全に理解する前に実装してしまったツケが回った形となりました…。
    ここまで詳しく教えて下さり、本当に恥ずかしいやら有難いやらですが…

    今後もっと勉強していきます。有難うございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる