まえがき
最近、Rails5でActionCable + Vue1を使ってSlackっぽい感じのチャット画面を作っているのですが…
- 「所属ルーム(Slackのチャンネルに相当)」と「ルーム内のメッセージ」を2ペインで表示する
- 「所属ルーム」と「メッセージ」のコンポーネントはそれぞれ独立したVueファイルに記述されている
という状況で
- 新規チャットメッセージが配信されたときに各コンポーネントでそれぞれ独立した処理を行いたい
- 例えば…
- 「メッセージ」ペインでは配信されたメッセージを末尾に追加する
- 「所属ルーム」ペインではLINEみたいに新規メッセージが来たルームを一番上に持っていく
- etc...
- 例えば…
上記を実現する必要がありました。2
そんな時にタイトルの件を行ったら良い感じに出来たと思いましたので共有します。
おことわり
- 各コンポーネント間のイベントの動作順序は問わない前提です
- 例えば「メッセージペインで行う処理が所属ルームペインで行う処理より後でなければいけない」場合は想定していません
- 他にも意外なところでマズいかもしれないのでご利用は自己責任でお願いします
- 筆者はActionCable,Vue共々始めて日が浅い1ので他に良い手法をご存知の方はコメントいただけると幸いです
書いたもの
まず、この様なJSファイルを用意しました。
chat-event.js
import ActionCable from 'actioncable'
const EventEmitter = require('events').EventEmitter
class ChatEventEmitter extends EventEmitter {
constructor() {
super()
const _this = this
this.cable = ActionCable.createConsumer('/cable')
this.channel = this.cable.subscriptions.create({channel: "ChatChannel"}, {
connected: function(){
_this.emit('connected')
},
disconnected: function(){
_this.emit('disconnected')
},
received: function(data){
_this.emit('received', data)
}
})
}
}
const instance = new ChatEventEmitter()
Object.freeze(instance)
export default instance
そして、各Vueファイルで使うときはこの様になります。
example.vue
<template>
<!-- 略 -->
</template>
<script>
import ChatEvent from 'chat-event.js'
// 略
export default {
// 略
}
ChatEvent.on('received', (data) => { // 配信されたときに呼び出される
console.log(data) // => コンソールに配信された内容が表示される
// 以下、行いたい処理
})
// ちなみにChatChannelのアクションを呼び出したいときは以下の様になる
// ChatEvent.channel.perform(...)
</script>
これで期待した動作が実現できました。
コンポーネントの分離性を損なわず、ActionCableのコールバックを利用したい際に使えるので今のところ気に入っています。
あとがき
数種類のチャンネルを貼りたいときは各チャンネルで同様のクラスを作ると良いと思います。
誰かが既にやっててもおかしくないのに誰もやってなさそうなあたりベストプラクティスではない気もする。
参考にしたページ
[Javascript] イベント駆動型の設計ができるEventEmitterに入門
JavaScript/ES6の基本文法とSingletonパターン | 酒と涙とRubyとRailsと