こちらの記事を参考にサイトのボタンを非同期で表示できるようにした。
https://whatsupguys.net/programming-school-dive-into-code-learning-82/
likeのようないいねボタンではなく、ありがとうを示すボタンのため、
下記よりthanksの文字が出てくるが、これは全てlikeから置き換わった内容です。
手順
(当記事ではアソシエーションなどが組まれていることを前提とします。)
(javascriptはjsファイルではなく、js.erbを使用し非同期通知を実装しています)
1. 非同期で呼び出したい親要素にIDを付ける。
2. link_to要素に非同期通信のためのオプションを付ける。
3. javascriptファイルへのアクセスの記述をコントローラー内に入れる。
4. jacascriptファイルに非同期通信する部分テンプレートを読ます。
【1】 jsで呼び戻される際の目印としてID要素で囲い部分テンプレートとして切り離しておく。
(#thanks_buttonを含めた子要素が非同期部分として対応する。
非同期通信させたい箇所を部分テンプレートとして切り離しておく。
その際にID要素を作り部分テンプレートを子要素とする構造を作っておく。
こちらの部分 => #thanks_button
.message-box__evaluation-flame
#thanks_button
= render partial: 'thanks/thank', locals: { message: message }
【2】 非同期通信のためのオプションを追加する
部分テンプレート内ではクリックの際に対象となるlink要素(ページ内ではボタンの表示)に
オプションでremote: trueを記述。
= link_to 'ありがとう', user_message_thanks_path(user_id: message.user_id, message_id: message.id), method: :post, remote: true
先に完成後の画像だが、こちらの部分(完成動画) > クリック
補足説明↓
remote: trueとは?
link_to, form_for などに対してオプションで設定することができ、リクエストがhtml形式ではなくjs形式になる。
Railsで remote: true と js.erbを使って簡単にAjax
他、ボタンの表示切り替えはビューファイル側でif文で切り替えています。
- if message.thank.blank? && message.user_id != current_user.id && message.chat_room.topic_create_user_id != message.user_id
#thanks-btn
= link_to 'ありがとう', user_message_thanks_path(user_id: message.user_id, message_id: message.id), method: :post, remote: true
- elsif message.thank.present? && message.user_id != current_user.id && message.chat_room.topic_create_user_id != message.user_id
#thanks-btn-aledy
= link_to '✓ ありがとう'.html_safe, user_message_thank_path(user_id: message.user_id, message_id: message.id, id: message.thank.id), method: :delete, remote: true
【3】 jsファイルにアクセスする。
thanksボタンが画面上で押されてcreate若しくはdestroyの処理が終わった際にjsファイルをrenderさせる。
createもdestroyも同じ記述になるので読み込むファイル名は同じです。
class ThanksController < ApplicationController
before_action :authenticate_user!
before_action :set_message
def create
Thank.create(user_id: params[:user_id], message_id: params[:message_id])
render 'thank_ajax.js.erb'
end
def destroy
thank = Thank.find(params[:id])
thank.destroy
render 'thank_ajax.js.erb'
end
private
def set_message
@message = Message.find(params[:message_id])
end
end
【4】 jsファイルにアクセスする。
【1】で説明した通り、#thanks_buttonを含めた子要素がhtmlとしてレンダリングされる。
その際にhtmlに反映する値も渡さないとエラーになるので【2】のset_messageで持っている@messageをmessageに渡し_thank.hamlファイルに渡す。
その記述がこちらの部分 => locals: {message: @message}
escape_javascript
の部分は良い説明文なく私もよく理解できておりませんが、
とりあえず必要になります。
$("#thanks_button").html("<%= escape_javascript(render partial: 'thanks/thank', locals: {message: @message}) %>");
更新 : 上記からの変更(問題が生じたため)
更新内容
-
変更前
thanksボタンを押した際、ajax通信で帰ってくるview中のthanksボタンのpathがきちんと指定されて反映されておらず、異なるmessage_idに対してthanksがcreateされていた。 -
変更後
下記の内容で修正をおこなった。
.thank-ajax{:id => "thanks-btn-#{message.id}"}を追加。
それぞれのmessage.idに合わせてthank_ajax.js.erbが適切に当たるようにした。
↓
.thank-ajax{:id => "thanks-btn-#{message.id}"}
- if message.thank.blank? && message.user_id != current_user.id && message.chat_room.topic_create_user_id != message.user_id
.thanks-btn
= link_to 'ありがとう', user_message_thanks_path(user_id: message.user_id, message_id: message.id), method: :post, remote: true
- elsif message.thank.present? && message.user_id != current_user.id && message.chat_room.topic_create_user_id != message.user_id
.thanks-btn-aledy
= link_to '✓ ありがとう'.html_safe, user_message_thank_path(user_id: message.user_id, message_id: message.id, id: message.thank.id), method: :delete, remote: true
render 'thank_ajax.js.erb'
された際にviewファイル側の{:id => "thanks-btn-#{message.id}"}
と
thank_ajax.js.erb'中の<%= @id_name %>
で2つのid名を合致させる必要があるため、before_action :set_variables
で @id_name = "#thanks-btn-#{@message.id}"
を実行しthank_ajax.js.erb
に渡す。
↓
class ThanksController < ApplicationController
~中略~
before_action :set_variables
def create
Thank.create(user_id: params[:user_id], message_id: params[:message_id])
render 'thank_ajax.js.erb'
end
~中略~
private
def set_message
def set_variables
@message = Message.find(params[:message_id])
@id_name = "#thanks-btn-#{@message.id}"
end
end
@id_name
で受け取る。(中身は@id_name = "#thanks-btn-#{@message.id}")
これでview側の= "#thanks-btn-#{@message.id}"
と合致するため、ajax通信できちんと
view側にrender partial: 'thanks/thank'
される
$("<%= @id_name %>").html("<%= escape_javascript(render partial: 'thanks/thank', locals: {message: @message}) %>");