Action Cableでリアルタイムコメント機能(1/2)


★Action Cableでリアルタイムコメント機能を実装する

新たにチャットルームは作成せずに投稿に紐づいたコメント機能として
リアルタイムでコメントの投稿、削除が可能な実装を行いました。

今回はこの実装のベースとなるコメント機能の基礎を実装します!
Action Cableでリアルタイムコメント機能(2/2)へ

★実装に必要な基礎知識

Action Cableとは
通常のRailsのアプリケーションと同様の記述で、即時更新機能を実装できるフレームワークです。実装内容としては、メッセージの保存や送信に必要なRubyのコーディングと、保存したメッセージを即時に表示させるJavaScriptのコーディングです。

Channelとは
チャネルとは、即時更新機能を実現するサーバー側の仕組みのことをいいます。
上記に示した通り、データの経路を設定したり、送られてきたデータをクライアントの画面上に表示させたりします。

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

broadcastとは、サーバーから送られるデータの経路のことを指します。
broadcastを介してデータをクライアントに送信します。

ClientやChannel(.js/.rb)Server ※イメージ

★コメント機能の基礎を実装

❶routingの記述を行う

アクションはcreateとdestroyを使用する
投稿に紐づかせたいのでpostsとネストしてあげる

#routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: { registrations: 'users/registrations' }
  root to: "posts#index"
  resources :users
  resources :posts do
    resources :comments, only: [:create, :destroy]
  end
end

❷controllerの記述を行う

投稿ページにコメントを表示させる為、comments_controllerには
コメントのcreateとdestoryに関する記述のみをする

#comments_controller.rb
class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    #投稿に紐づいたコメントを作成
    @comment = @post.comments.build(comment_params)
    @comment.user_id = current_user.id
    if @comment.save
      ActionCable.server.broadcast 'message_channel', content: @comment, user: @comment.user, date: @comment.created_at.to_s(:datetime_jp), id: @comment.id,post: @comment.post
    end
  end

  def destroy
    @post = Post.find(params[:post_id])
    @comment = Comment.find(params[:id])
    if @comment.destroy
      ActionCable.server.broadcast 'delete_channel', id: @comment.id
    end
  end

  private
  def comment_params
    params.require(:comment).permit(:content, :post_id, :user_id).merge(user_id: current_user.id)
  end
end

posts_controllerにはコメントを表示させる為の記述を行う
今回は投稿に詳細ページを実装しているのでアクションはshow

#posts_controller
  def show
    @post = Post.find(params[:id])
    @comment = Comment.new
    @comments = @post.comments
  end

❸viewの記述を行う

renderを利用してコメントの表示部分とコメントの投稿部分を呼び出す

#show.html.erb
   <div class="row">
     <ul>
       <li class="comment-create">
         <h3 class="text-left title">トークルーム</h3>
       </li>
       <li class="comments_area">
         <%= render partial: 'comments/index', locals: { comments: @comments } %>
       </li>
     </ul>
     <% if user_signed_in? %>
       <div id="comment-create">
         <h3 class="text-left">コメントを投稿</h3>
           <%= render partial: 'comments/form', locals: { comment: @comment, post: @post } %>
       </div>
     <% end %>
    </div>

コメント表示部分

#_index.html.erb
    <% @comments.each do |comment| %>
      <% unless comment.id.nil? %>
        <li class="comment-container" id="test-<%=comment.id%>">
          <div class="comment-box">
            <div class="comment">
              <div class="comment-nickname">
                <p><%= link_to "@#{comment.user.nickname}", user_path(comment.user.id) %></p>
              </div>
                <div id="comment-entry">
                  <span style="font-weight:bold;"><%= comment.content %></span>
                    &nbsp;<%= comment.created_at.to_s(:datetime_jp) %>
                      <% if comment.user == current_user %>
                        <%= link_to post_comment_path(comment.post_id, comment.id), method: :delete, remote: true do %>
                          &nbsp;<button id="<%=comment.id%>" id="delete-btn">削除</button>
                        <% end %>
                      <% end %>
                </div>
              </div>
           </div>
        </li>
      <% end %>
    <% end %>
<!-- コメント内容(3件目以降) ------------------------------------------------------------------>
    <div class="collapse" id="collapseExample">
      <% @comments.offset(2).each do |comment| %>
        <% unless comment.id.nil? %>
          <li class="comment-container" id="test-<%=comment.id%>">
            <div class="comment-box">
              <div class="comment">
                <div class="comment-nickname">
                  <p><%= link_to "@#{comment.user.nickname}", user_path(comment.user.id) %></p>
                </div>
                  <div id="comment-entry">
                    <span style="font-weight:bold;"><%= comment.content %></span>
                      &nbsp;<%= comment.created_at.to_s(:datetime_jp) %>
                        <% if comment.user == current_user %>
                          <%= link_to post_comment_path(comment.post_id, comment.id), method: :delete, remote: true do %>
                            &nbsp;<button id="<%=comment.id%>" id="delete-btn">削除</button>
                          <% end %>
                        <% end %>
                  </div>
              </div>
            </div>
          </li>
        <% end %>
      <% end %>
    </div>

コメントの投稿部分

#_form.html.erb
<%= form_with(model: [@post, @comment], url: post_comments_path(@post.id) ) do |f| %>
    <%= f.text_area :content, class: "input-mysize" %>
    <%= f.submit "送信", class: "btn btn-outline-dark comment-submit float-right", id:"submit_btn" %>
<% end %>

modelとmigrationファイルは省略します!

Action Cableでリアルタイムコメント機能(2/2)へ