LoginSignup
2
1

More than 1 year has passed since last update.

ActionCableを用いたチャットアプリでチャットルームとメッセージを紐づける方法

Last updated at Posted at 2022-03-20

概要

 先日、こちらの記事で解説されていたActionCableを用いたリアルタイムチャットアプリを動かしてみました。一通り動かせたのでこれを発展させて、複数のチャットルームを作りそれぞれに対してメッセージを投稿できるようにしてみたのでその方法をまとめました。

実装

こちらの記事を前提とします。

最初に こちらの記事の通りにgonというgemをインストールします。これはコントローラからjavascriptへの変数の受け渡しが簡単に行えるようにするものです。

 ER
 

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で取得/購読解除する

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1