Action Cableの習作: シンプルなチャット
外観
前回のアプリはメッセージを送受信するだけでDBにメッセージを保存していませんでした。今回はDBにメッセージを保存するチャットアプリを作成したいと思います。
リポジトリ
環境
macOS 10.15.4
Ruby 2.7.1
Rails 6.0.3
Yarn 1.22.4
Node 13.12.0
参照
Rails 5: Action Cable demo - YouTube
Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より) - Qiita
Rooms#showでのMessageの表示
Rails new
rails new -MT --skip-active-storage try_ac_simple_chat
cd try_ac_simple_chat
Generate rooms controller
bin/rails g controller rooms show
Root to Rooms#show
config/routes.rb
Rails.application.routes.draw do
root 'rooms#show'
end
Generate Message model
bin/rails g model Message content:text
bin/rails db:migrate
Add Message seeds
db/seeds.rb
Message.create! content: 'Hello world!'
bin/rails db:seed:replant
Show Message in Rooms#show
app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
def show
@messages = Message.all
end
end
mkdir app/views/messages
app/views/messages/_message.html.erb
<div class="message">
<p><%= message.content %></p>
</div>
app/views/rooms/show.html.erb
<h1>Chat room</h1>
<div id="messages">
<%= render @messages %>
</div>
動作確認
bin/rails s
Roomチャンネルの作成
Generate RoomChannel
bin/rails g channel room speak
Add speak IF to RoomChannel
app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
# stream_from 'room_channel'
stream_from 'room:message'
end
# ...
def speak(data)
# ActionCable.server.broadcast 'room_channel', message: data['message']
RoomChannel.broadcast_to('message', data)
end
end
Add speak and receive IF to consumer
app/javascript/channels/room_channel.js
// consumer.subscriptions.create("RoomChannel", {
window.room = consumer.subscriptions.create("RoomChannel", {
// ...
received(data) {
alert(data['message'])
},
speak(message) {
return this.perform('speak', { message: message })
},
});
動作確認
ウェブブラウザをリロードする。
room.speak('Hello world')
アラート表示を確認する。
Add form to Rooms#show
app/views/rooms/show.html.erb
<form>
<label>Say something:</label><br>
<input type="text" data-behavior="room_speaker">
</form>
動作確認
ウェブブラウザをリロードする。
Send message from form
app/javascript/channels/room_channel.js
const roomChannel = consumer.subscriptions.create("RoomChannel", {
// ...
})
document.addEventListener('turbolinks:load', () => {
const els = document.querySelectorAll('[data-behavior~=room_speaker]')
els.forEach(el => {
el.addEventListener('keypress', event => {
if (event.keyCode === 13) {
roomChannel.speak(event.target.value)
event.target.value = ''
event.preventDefault()
}
})
})
})
動作確認
ウェブブラウザをリロードする。
メッセージを入力しアラート表示を確認する。
メッセージの保存とブロードキャストの非同期化
Save Message on RoomChannel#speak
app/channels/room_channel.rb
# ...
def speak(data)
# ActionCable.server.broadcast 'room_channel', message: data['message']
# RoomChannel.broadcast_to('message', data)
Message.create! content: data['message']
end
end
app/models/message.rb
class Message < ApplicationRecord
after_create_commit { MessageBroadcastJob.perform_later self }
end
Generate MessageBroadcastJob
bin/rails g job MessageBroadcast
Add render_message on MessageBroadcastJob
app/jobs/message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
# ActionCable.server.broadcast 'room_channel', message: render_message(message)
RoomChannel.broadcast_to('message', render_message(message))
end
private
def render_message(message)
# ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
ApplicationController.render(partial: 'messages/message', locals: { message: message })
end
end
app/javascript/channels/room_channel.js
received(data) {
// alert(data['message'])
alert(data)
},
動作確認
ウェブブラウザをリロードする。
メッセージを入力しアラート表示を確認する。
メッセージの画面表示
Append message in Rooms#show
app/javascript/channels/room_channel.js
// ...
received(data) {
const el = document.getElementById('messages')
el.insertAdjacentHTML('beforeend', data)
},
動作確認
ウェブブラウザをリロードする。
メッセージを入力する。メッセージが画面に追加されることを確認する。
他のウィンドウから入力したメッセージが表示されることを確認する。
以上です。
この記事が気に入ったらサポートをしてみませんか?