LoginSignup
23
20

More than 3 years have passed since last update.

js.erbを使って非同期通信(いいね機能を10分で実装)

Last updated at Posted at 2019-05-19

こちらの記事を参考にサイトのボタンを非同期で表示できるようにした。
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

html.haml
.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文で切り替えています。

_thank.haml
- 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も同じ記述になるので読み込むファイル名は同じです。

thanks_controller.rb
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 の部分は良い説明文なく私もよく理解できておりませんが、
とりあえず必要になります。

thank_ajax.js.erb
$("#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が適切に当たるようにした。

app/views/thanks/_thank.haml
.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 '&#10003; ありがとう'.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に渡す。

app/controllers/thanks_controller.rb
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'される

app/views/thanks/thank_ajax.js.erb
$("<%= @id_name %>").html("<%= escape_javascript(render partial: 'thanks/thank', locals: {message: @message}) %>");
23
20
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
23
20