Ruby on Rails: チャットメッセージ実装(自動更新機能まで)...chapter18-2
①タイムゾーンの設定
2020年9月1日みたいな感じで投稿した日にちを表示したいのでタイムゾーンを設定していきます。
config/application.rb
config.time_zone= "Tokyo"
config.active_record.default_timezone= :localを設定します。
↓
$rails s
サーバーを再起動します。
②chat_messages_controller.rbの編集
app/controllers/chat_messages_controller.rb
@messagesにチャットグループでのメッセージを代入します。
③index.html.erbのビューを編集
https://gyazo.com/6781ccd0fa45a0b85d1f9b21fe9567f7
https://gyazo.com/89f484d0508d9edd20bf7de4dd206382
↓
https://gyazo.com/0c6b1342f1c00691713c797fcafa2d7b
https://gyazo.com/7dd81829b992ee348ce8fd6122b89ce6
とりあえずメッセージが投稿されるようになりました。
しかし非同期通信はできていないので、投稿したときに
非同期通信をして部分的更新をして更新速度を上げるのと、2窓してみて自動更新できるようにしていきたいと思います。
④app/controllers/api/chat_messages_controller.rbを作成
$rails g controller api/chat_messages
↓
https://gyazo.com/4eb0e8e76d30d2989d0adf1bbbbc99e1
↓
これでApplicationControllerはApi::ChatMessagesControllerを継承できるようになりました。
これで、このコントローラーへコードしたことはapiを使用したもののみを対応させて動かすことができます。
後でここに投稿した後に投稿されたメッセージが表示されるように処理内容をコードします。
app/views/chat_messages/create.json.jbuilder
app/controllers/chat_messages_controller.rbのcreateアクションでインスタンス変数へ代入したメッセージ情報をjsonへ変換します。
app/views/api/chat_messages/index.json.jbuilder
忘れないようにこちらのjbuilderファイルも作成しておきましょう。
私はこのファイルを作成し忘れていて配列を用意したいのにできず2日悩みました。←慣れていれば、こんなことで悩むことはないと思うのですが😂 (笑)
⑥app/controllers/api/chat_messages_controller.rbを編集
@groupのインスタンスへChatGroupモデルを通してfindメソッドでchat_group_idを探してきて、投稿しているチャットグループの情報を代入します。
@messages = @group.chat_messages.includes(:user).where('id > ?, params[:id]')
params[:id]...これはchat_messages.jsでajaxによって送られてきたidです。
チャットメッセージで最後に投稿されたメッセージのidよりも大きいidのメッセージがないか探してきてインスタンス変数@messagesへ代入します。
includes(:user)はN+1問題の対策のために使用します。
N+1問題を解決する理由としては同じ繰り返し処理を減らしてSQL発行を減らそうという考えです。
このincludesがなければ、「投稿されたメッセージは誰のなの???」とchatmessagesテーブルとusersテーブルの間を行ったり来たりして探すという作業が行われます。
なので投稿されたメッセージ分だけusersテーブルを行き来します。
これは無駄な作業です。
これではメッセージが増えれば増えるほどSQLへの負担が大きくなります。
投稿されたメッセージをまとめてusersテーブルへ探しに行けるのがincludesメソッドです。
https://qiita.com/TsubasaTakagi/items/8c3f4317ad917924b860
そしてrespond_to do |format|~endまででhtmlをjsonへ変換できるようにしておきます。
⑦chat_messages.jsファイルを作成
app/javascript下へchat_messages.jsファイルを作成します。
⑧app/javascript/packs/application.jsを編集
chat_messages.jsファイルを使用できるように↓を追加します。
これでchat_messages.jsファイルを使用できるようになりました。
⑨app/controllers/chat_messages_controller.rbを編集
createメソッドでjsonへ変換できるようにしておきます。
⑩app/javascript/chat_messages.jsを編集
注意することは、⑥でparams[:id]としたこと。
これは$.ajax({ ~ }).done({ ~ data: {id: 〇〇})←この太字にした部分と同じでないといけません。
なのでdata:{last_id: 〇〇}とした場合、⑥はparams[:last_id]とします。
[ 1 ]
https://gyazo.com/99ef582eb08084f5ddb789763fa700c1
[ 2 ]
https://gyazo.com/a48a1d2c55da565e1158f03380732abc
[ 3 ]
https://gyazo.com/5e6ae49f2ca2da5dd68a189d152f501c
[ 1 ]でここでメッセージを投稿した時に、追加するhtmlを作っておきます。
・変数message_idに投稿されたメッセージのidを格納
・変数user_iconにもしユーザーアイコンがあるならばアイコンを、なければデフォルトのアイコンを格納
・変数chat_messageにメッセージがあるなら格納し、なければ入れない。
・変数imageもchat_messageと同様。画像があるかないかによって処理を分けてます。
・変数dateへupdated_atを格納
・変数htmlへ投稿したときの追加内容を格納
[ 2 ]でメッセージ投稿処理をしています。
・e.preventDefault()で投稿ボタンを押すまで処理を待ってくれます。
https://qiita.com/tochiji/items/4e9e64cabc0a1cd7a1ae
・変数formDataへformタグの開始から終了までの処理内容を入れてインスタンス変数を格納
・変数urlにメッセージページのurlを格納
・done(function(data){・・・})でdataは投稿された内容を格納しています。これを結果で返します。
$('form')[0].reset();...これでメッセージを投稿後リセットされます。
https://pikawaka.com/javascript/form-reset
$('#chat-messages-btn').prop('disabled', false);...ボタンが再度有効になります。
https://qiita.com/pugiemonn/items/5db6fb8fd8a303406b17
・変数iへ送ったメッセージが表示されているトップの座標を格納
・$('html, body').animate({scrollTop:i})...animateメソッドを使用しスクロール動作できるようにします。その際、垂直にスクロールしたいのでscrollTopを使用し垂直にスクロールするためのピクセル数を取得します。
https://itsakura.com/jquery-scroll
https://www.sejuku.net/blog/61689
[ 3 ]でメッセージを最新状態に保つための処理をしています。
・変数last_message_idに最新の(最後の)メッセージidを取得。
・if文内容でこのメッセージページと一致した場合にこの中の処理が実行できるようにします。このif文がないと他のチャットグループのメッセージを開いているとエラーがでます。
・変数insertHTMLへ空の変数を定義
・messages.forEach(function(message){})...messagesをforEachで繰り返しし取り出し処理して、この中での処理内容を引数messageへ渡します。
・insertHTML=buildHTML(message);...投稿されたメッセージ結果を格納
・$('.chat-messages__conversations').append(insertHTML);...insertHTMLに格納された処理内容を<div class="chat-messages__conversations">~</div>へ追加。
・setIntervalは一定の処理を繰り返すメソッドです。これで、自動更新機能を実装できます。7000は一定処理を繰り返す時間を設定しています(今回は7000ミリ秒=7秒間隔で処理しています)。
https://techacademy.jp/magazine/5537
[ 1 ]でのreturn htmlに関しては、これをコードしておかないと追加できなくなります。
returnは関数処理した結果を戻り値として返せます。
https://www.javadrive.jp/javascript/function/index4.html
↓
https://gyazo.com/6704d1f3533a3d085fd1db3fd78cfa17
https://gyazo.com/7ef7c0e2efc4a874c12fcf0d6a36028d
非同期機能と自動更新機能を実装しました。
実装時のポイントはconsole.logを使用して処理内容を確認しながら作るってことでしょうか😅(例)console.log(this);)。
以上です。