概要
先日、こちらの記事で解説されていたActionCableを用いたリアルタイムチャットアプリを動かしてみました。一通り動かせたのでこれを発展させて、複数のチャットルームを作りそれぞれに対してメッセージを投稿できるようにしてみたのでその方法をまとめました。
実装
※ こちらの記事を前提とします。
最初に こちらの記事の通りにgonというgemをインストールします。これはコントローラからjavascriptへの変数の受け渡しが簡単に行えるようにするものです。
config\routes.rb
Rails.application.routes.draw do
root to: 'rooms#show'
resources :rooms
end
app\views\rooms\index.html.slim
- @rooms.each do |room|
li
= link_to "#{room.id}", room_path(room.id)
= link_to "room作成", new_room_path
app\views\rooms\show.html.slim
h1
| Chat room
#messages data-room_id= @room.id
= render @messages
form
label
| Say something:
br
input[type="text"]
app\views\messages_message.html.erb
<div class="message">
<p><%= message.content %></p>
</div>
app\controllers\rooms_controller.rb
class RoomsController < ApplicationController
def show
@room = Room.find(params[:id])
gon.room = @room
@messages = @room.messages
end
def index
@rooms = Room.all
end
def new
@room = Room.create
redirect_to rooms_path
end
def create
end
end
app\models\room.rb
class Room < ApplicationRecord
has_many :messages
end
app\models\message.rb
class Message < ApplicationRecord
belongs_to :room
after_create_commit { MessageBroadcastJob.perform_later self }
end
app\javascript\channels\room_channel.js
import consumer from "./consumer"
let room_id = gon.room.id;
const appRoom = consumer.subscriptions.create({channel: "RoomChannel", room_id: room_id}, {
received(data) {
const messages = document.getElementById('messages');
messages.insertAdjacentHTML('beforeend', data['message']);
},
speak: function(message) {
return this.perform('speak', {message: message, room_id: room_id});
}
});
window.addEventListener("keypress", function(e) {
if (e.keyCode === 13) {
appRoom.speak(e.target.value);
e.target.value = '';
e.preventDefault();
}
})
//ルームを離れる直前に行う処理
//明示的に購読を解除する
window.onbeforeunload = function(){
let subscriptions = consumer.subscriptions['subscriptions'];
subscriptions.forEach(function(subscription){
consumer.subscriptions.remove(subscription);
});
}
app\channels\room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel_#{params[:room_id]}"
end
def unsubscribedunsubscribed
end
def speak(data)
@room = Room.find_by(id: data['room_id'])
@room.messages.create(content: data['message'])
end
end
app\jobs\message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
ActionCable.server.broadcast "room_channel_#{message.room_id}", message: render_message(message)
end
private
def render_message(message)
ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
end
end
参考資料
【Rails6.0】ActionCableを使用したライブチャットアプリを実装する手順を解説
Rails 5 Action Cable メッセージとルームを紐付ける。
【JavaScript】data属性の取得・設定・更新【dataset】
Action Cable の概要
【Rails】gonっていうRailsで定義した変数をJSで使えるgem
【ActionCable】購読中のチャンネルをjsで取得/購読解除する