なぜやろうと思ったか
railsで非同期通信を行った時に、自分のwebブラウザしか結果が反映されない問題を解消したかった。
前まで、setIntervalで10秒ごとに読み込みさせて認識させていたんですが、変化がない時にも動くので何か他の方法がないかなぁ〜と模索していました。
完成コード
tableはmessage,column名はbodyでdbを作成
class ChatMessageChannel < ApplicationCable::Channel
def subscribed;stream_from "chat_message_channel";end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def speak(data)
@message = Message.create(body: data["message"])
data['id'] = @message.id
ActionCable.server.broadcast 'chat_message_channel',message: data
end
end
App.chatmessage = App.cable.subscriptions.create("ChatMessageChannel", {
connected: function() {
// Called when the subscription is ready for use on the server
},
disconnected: function() {
// Called when the subscription has been terminated by the server
},
received: function(data) {
let message_id = data['message']['id']
let message_text = data['message']['message']
let html =`
<tr>
<td><a href="/messages/${message_id}">Show</td>
<td><a href="/messages/${message_id}/edit">Edit</td>
<td><a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href="/messages/${message_id}">Destroy</td>
<td>${message_text}</td>
</tr>`
$("#message").append(html)
// Called when there's incoming data on the websocket for this channel
},
speak: function(message) {
return this.perform('speak',{message: message});
}
}, $(document).on('keypress', '[data-behavior~=speak_chat_messages]', function(event) {
if (event.keyCode === 13){
App.chatmessage.speak(event.target.value)
event.target.value = ''
event.preventDefault()
}
}));
rails g scaffoldで作成したindex.html.erbにidなど少し組み込んだ
<p id="notice"><%= notice %></p>
<h1>Messages</h1>
<table>
<thead>
<tr>
<th colspan="3"></th>
</tr>
</thead>
<tbody id="message">
<% @messages.each do |message| %>
<tr>
<td><%= link_to 'Show', message %></td>
<td><%= link_to 'Edit', edit_message_path(message) %></td>
<td><%= link_to 'Destroy', message, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<td><%= message.body %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Message', new_message_path%>
</div>
<br/>
<div class="aaa"><%= @message.body %></div>
<form>
<label>
websocketsで送信: <input type="text" data-behavior="speak_chat_messages">
</label>
</form>
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
主にこの4つのfileをいじってもらいことになりますね。
cable.js
まず最初に読まれるfileです。
クライアントからサーバーに対してWebSocket接続しているコードで、
App.cable = ActionCable.createConsumer();でWebSocket通信を確立している。
他のサイトにはcoffee fileを呼ぶものがありますが、今回はmessage.js
に記述を書いていきます。
message.js
App.chatmessage = App.cable.subscriptions.create("ChatMessageChannel", {
でcable.jsからmessage.jsに接続している。
成功すれば、connectedに飛び、失敗したもしくは拒否した場合はdisconnectedに飛ぶ。
その後、inputのkeypress functionを動かす。
enterキーを押すと,if (event.keyCode === 13)の条件がtrueになる。
keycodeとは?
http://shanabrian.com/web/javascript/keycode.php
キーボードを押すと、キーコードが発行される。enterは13番。
用いればどのキーボードを押したかがわかる。
preventDefaultでrailsのhttp通信を中断し、その代わりにjqueryのApp.chatmessage.speak function
を実行する。引数にevent.target.value(
keypressしたinputの中身)を定義。
次に、speak: function(message) が呼ばれます。
return this.performでサーバーのChat_message_Channel#speak
を呼び出す。
仮に、perform('appear',message: message)なら Chat_message_Channel#appearを呼び出す。
第二引数には渡したい値をハッシュで渡してあげます。
{performはrailsのメソッドだと思われ、サブスクリプションでperformメソッドを使って、RPC(リモートプロシージャコール)として利用できるみたい}
よくわかりません。。。。(・・;)
https://railsguides.jp/action_cable_overview.html
Chat_message_channel.rb
speak actionが呼ばれました。ここでは、railsのcontrollerの役割を担う部分になります。
情報をdbにsaveしたりできます。
speak(data)
におけるdataの中身はハッシュで受け取ってます。params的な感じで。
data=> {"message"=>"111111", "action"=>"speak"}
今回は、saveした@message.idをviewで使用するので、dataのハッシュにidを入れ込みます。
この時点でのdataはこんな感じ。
data=> {"message"=>"111111", "action"=>"speak", "id"=>125}
speakメソッドのActionCable.server.broadcast
では第1引数にチャネル名、第2引数に発言メッセージを指定することで、サーバー側からChatMessageChannelにWebSocketで接続している全クライアントに対して発言メッセージを配信することが可能になります。
これで、全クライアントに対して処理を行う準備ができました。
メッセージを配信したので、受け取りを行わなければなりませんね。
message.js
message.jsに戻ってきました。
配信されたdataをここのreceived functionで受け取ります。
message: {message: "1111", action: "speak", id: 128}
__proto__: Object
dataはこんな感じなので取り出し方は、data['message']['id']
みたいな感じ。
その後、作成したhtmlを追加する処理を書いて終了となります。。
最後に
参考にした記事にはcoffee fileを用いて使う手法もありましたが、利便性が悪いのでjqueryを使用してやってみました。
これで、リアルタイムのchatが作れるよ〜〜〜。
参照した記事
https://railsguides.jp/action_cable_overview.html
https://codezine.jp/article/detail/10153?p=1
https://qiita.com/fujisawaryohei/items/3a567a64691276da0d45