search
LoginSignup
9

posted at

updated at

Action Cableでできることと実装方法

※2022年から技術系の記事は個人ブログに投稿しております。ぜひこちらもご覧ください→yamaday0u Blog

Action Cableは、Railsのアプリケーションと同様の記述で、WebSocket通信という双方向の通信によるリアルタイム更新機能を実装できるフレームワークで、Rails5から実装されました。

Action Cableを利用することで、たとえばリアルタイムで更新されるチャット機能を実装することができます。

チャネルの作成

channel

リアルタイム更新機能を実現するサーバー側の仕組みでデータの経路を設定したり、送られてきたデータを画面上に表示させたりします。

###チャネルを作成するコマンド

ターミナル

rails g channel message #チャネル名
# ↓実行結果↓
Running via Spring preloader in process *****
      invoke  test_unit
      create  test/channels/message_channel_test.rb
      create  app/channels/message_channel.rb
   identical  app/javascript/channels/index.js
   identical  app/javascript/channels/consumer.js
      create  app/javascript/channels/message_channel.js

生成されるファイルのうち以下の2つが重要です。
app/channels/message_channel.rb → クライアントとサーバーを結びつけるためのファイル
app/javascript/channels/message_channel.js → サーバーから送られてきたデータをクライアントに描画するためのファイル

クライアントとサーバーを結びつける

stream_fromメソッド

サーバーとクライアントを関連づけるメソッドでAction Cableにあらかじめ用意されています。

app/controller/messages_controller.rb
class MessageChannel < ApplicationCable::Channel
  def subscribed
    stream_from "message_channel" #記述箇所
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end

broadcast

stream_fromメソッドで関連付けられるデータの経路で、サーバーから送られるデータの経路です。broadcastを介してデータをクライアントに送信します。

broadcastを使ってコントローラーへ以下のように記述します。

app/controller/messages_controller.rb
class MessagesController < ApplicationController
  def new
    @messages = Message.all
    @message = Message.new
  end

  def create
    @message = Message.new(text: params[:message][:text])
    if @message.save
      ActionCable.server.broadcast 'message_channel', {content: @message}
    end
  end
end

broadcastという経路を通して、message_channelに向けて@messagecontentに入れてデータを送信しています。

JavaScriptへの記述

サーバーから送信されたデータはdataの中にcontentという名称で入っているので、data.content.textで使用できます。

app/javascript/channels/message_channel.js
import consumer from "./consumer"

consumer.subscriptions.create("MessageChannel", {
  received(data) {
    // 以下で使用
    const html = `<p>${data.content.text}</p>`;
    const messages = document.getElementById('messages');
    const newMessage = document.getElementById('message_text');
    messages.insertAdjacentHTML('afterbegin', html);
    newMessage.value='';
  }
});

ちなみにビューはこんな感じです。

app/views/messages/new.html.erb
<h3>mini-talk-app</h3>
<%= form_with model: @message do |f| %>
  <%= f.text_field :text %>
  <%= f.submit '送信' %>
<% end %>
<div id="messages">
  <% @messages.reverse_each.each do |message| %>
    <p><%= message.id %></p>
    <p><%= message.text %></p>
  <% end %>
</div>

content以外もクライアントに送信できる

app/controller/messages_controller.rbでは保存したメッセージの値をcontentに入れて送信していましたが、設定すれば他の値も送ることができます。

ぼくが今開発しているアプリでは、こんな感じで情報を追加してクライアント側に送信しています。

app/controllers/chats_controller.rb
class ChatsController < ApplicationController  def create
    @chat = Chat.new(chat_params)
    if @chat.save
      # userとtimeにそれぞれ@userと@timeを入れている
      @user = current_user
      @time = @chat.created_at.strftime("%Y-%m-%-d %-H:%-M")
      ActionCable.server.broadcast 'chat_channel', content: @chat, user: @user, time: @time
    end
  end

  private
  def chat_params
    params.require(:chat).permit(:text).merge(
      user_id: current_user.id,
      group_id: params[:group_id]
    )
  end
end

クライアント(JavaScript)側ではこのようにデータを利用しています。JavaScript側で投稿日時の表示形式を変更する方法が分からなかったので、事前にサーバー(Ruby)側で表示形式を変更してから送信しています。

app/javascript/channels/chat_channel.js
import consumer from "./consumer"

consumer.subscriptions.create("ChatChannel", {
  received(data) {
    // 送信されたデータを以下で使用
    const html = `<div class="each-chat">
                    <p class="chat-time">${data.time}</p>
                    <p class="chat-text">${data.content.text}</p>
                    <p class="chat-user">${data.user.name}</p>
                  </div>`;
    const chats = document.getElementById("chats");
    chats.insertAdjacentHTML("afterbegin", html);
    const newChat = document.getElementById("chat_text");
    newChat.value="";
  }
});

参考資料

【Rails6.0】ActionCableを使用したライブチャットアプリを実装する手順を解説

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
What you can do with signing up
9