流れ
⑴表示されているメッセージのHTMLにid情報を追加
⑵メッセージを更新するためのアクションを実装
⑶追加したアクションを動かすためのルーティングを実装
⑷追加したアクションをリクエストするよう実装
⑸取得した最新のメッセージをブラウザのメッセージ一覧に追加
⑹数秒ごとにリクエストするよう実装
⑺メッセージ分だけスクロールするよう実装
⑻メッセージ一覧のページでのみJSが動くよう実装
⑴表示されているメッセージのHTMLにid情報を追加
_message.html.haml
//メッセージ全体にidを付与。 ※#{message.id}はjbuilderファイルで定義している
.message{"data-message-id": "#{message.id}"}
⑵メッセージを更新するためのアクションを実装
コントローラーのapiディレクトリの中に、messages_controller.rbを作成
messages.controller.rb
#APIの中にありますよーって意味
class Api::MessagesController < ApplicationController
def index
#今いるグループの情報をparamsによって取得し変数@groupに代入
@group = Group.find(params[:group_id])
#グループ内のメッセージでlast_idよりも大きいidのメッセージがないかを探してきてそれらを@messageに代入
@messages = @group.messages.includes(:user).where('id > ?', params[:last_id])
end
end
⑶追加したアクションを動かすためのルーティングを実装
ルーティングの追加
routes.rb
Rails.application.routes.draw do
devise_for :users
root 'groups#index'
resources :users, only: [:index, :edit, :update]
resources :groups, only: [:new, :create, :edit, :update] do
resources :messages, only: [:index, :create]
#ここからが追加
namespace :api do
resources :messages, only: :index, defaults: { format: 'json' }
end
end
end
jbuilderファイルも作成
index.json.jbuilder
json.array! @messages do |message|
json.content message.content
json.image message.image
json.created_at message.created_at
json.user_name message.user.name
json.id message.id
end
⑷追加したアクションをリクエストするよう実装
message.js
//自動更新用の関数定義
let reloadMessages = function () {
//ブラウザに表示されている最後のメッセージからidを取得して、変数に代入
let last_message_id = $('.message:last').data("message-id");
//ajaxの処理
$.ajax({
//今回はapiのmessagesコントローラーに飛ばす
url: "api/messages",
//HTTP_メソッド
type: 'get',
//データはjson型で
dataType: 'json',
//キーを自分で決め(今回はlast_id)そこに先ほど定義したlast_message_idを代入。これはコントローラーのparamsで取得される。
data: {last_id: last_message_id}
})
//doneの処理(仮)
.done(function(messages) {
console.log('success');
})
//failの処理(仮)
.fail(function() {
console.log('error');
});
};
⑸取得した最新のメッセージをブラウザのメッセージ一覧に追加
doneの中身の編集
message.js
.done(function (messages) {
//追加するhtmlの入れ物をつくる
let insertHTML = '';
//取得したメッセージたちをEach文で分解
messages.forEach(function (message) {
//htmlを作り出して、それを変数に代入(作り出す処理は非同期の時に作った)
insertHTML = buildHTML(message);
//変数に代入されたhtmlをmessagesクラスにぶち込む
$('.messages').append(insertHTML);
})
})
buildHTMLを一部編集
message.js
function buildHTML(message){
let image = ( message.image ) ? `<img class= "lower-message__image" src=${message.image} >` : "";
//messageクラスにdata-message-idを付与
let html = `<div class="message", data-message-id="${message.id}">
<div class="upper-message">
<div class="upper-message__user-name">
${message.user_name}
</div>
<div class="upper-message__date">
${message.date}
</div>
</div>
<div class="lower-message">
<p class="lower-message__content">
${message.content}
</p>
${image}
</div>
</div> `
$('.messages').append(html);
}
⑹数秒ごとにリクエストするよう実装
message.js
//これにより5000ミリ秒ごとにreloadMessagesが呼び出される※処理の外に記述
setInterval(reloadMessages, 5000);
⑺メッセージ分だけスクロールするよう実装
message.js
//doneの処理内に記述。非同期の方でも定義しているので、関数にしてまとめた方がなお良い
$('.messages').animate({scrollTop: $('.messages')[0].scrollHeight}, 'fast');
⑻メッセージ一覧のページでのみJSが動くよう実装
if文で関数を挟む
message.js
//今いるページのリンクが/groups/グループID/messagesのパスとマッチすれば実行
if (window.location.href.match(/\/groups\/\d+\/messages/)){