🤖

【Rails】コメント機能の非同期通信化

2023/07/04に公開

コメント機能を非同期通信を用いて実装します。

実装要件

  • コメント一覧を部分テンプレートとすること
  • サーバーからのレスポンスでコメント一覧のテンプレートを返すこと
  • コメントの投稿後、コメントフォームの値に前回の投稿が残らないようにすること
  • form_withを使うこと

Viewファイルの作成

部分テンプレートファイルの作成、非同期通信する要素にID属性を追加します。
以下画像のように3枠に分けて作成します。

部分テンプレートファイル作成

①コメント数

app/views/book_comments/_counter.html.erb
コメント数: <%= book.book_comments.count %>

②コメント一覧

app/views/book_comments/_index.html.erb
<table>
  <tbody>
    <% book.book_comments.each do |book_comment| %>
    <tr>
      <td>
        <%= link_to user_path(book_comment.user) do %>
          <%= image_tag book_comment.user.get_profile_image, size: "50x50" %><br>
          <%= book_comment.user.name %>
        <% end %>
      </td>
      <td><%= book_comment.comment %></td>
      <td>
        <% if book_comment.user == current_user %>
          <%= link_to "Destroy", book_book_comment_path(book, book_comment), method: :delete, class: "btn btn-sm btn-danger float-end", remote: true %>
        <% end %>
      </td>
    </tr>
    <% end %>
  </tbody>
</table>

remote: trueは非同期通信を行うためのオプションです。Railsではデフォルトで非同期通信を処理するためのライブラリが組み込まれているため、実装することによりページがリロードされることなく変更を反映することができます。

③コメント投稿フォーム

app/views/book_comments/_form.html.erb
<%= form_with model: [book, book_comment], local: false do |f| %>
  <%= f.text_area :comment, rows:'5', placeholder: "コメントをここに", class: "w-100", id: "comment_textarea" %>
  <%= f.submit "送信" %>
<% end %>

form_withヘルパーではlocal: falseを指定することで、非同期通信を行うことができます。
id: "comment_textarea"を追加して、comment_textareaというIDがテキストエリアに割り当てられるようにします。

idを追加

showページ
app/views/books/show.html.erb
  <div class="container">
    <div class="row">
      <div class="col-md-3">
        <h2>User info</h2>
        <%= render "users/info", user: @user %>
        <h2 class="mt-3">New book</h2>
        <%= render "form", book: @books %>
      </div>
      <div class='col-md-8 offset-md-1'>
        <h2>Book detail</h2>
        <table class="table">
          <tr>
            <td><%= link_to @book.user do %>
                <%= image_tag @book.user.get_profile_image, size:"50x50" %><br>
                <%= @book.user.name %>
                <% end %>
            </td>
            <td><%= link_to @book.title, @book %></td>
            <td><%= @book.body %></td>
            <td id="favorite_btn_<%= @book.id %>">
              <%= render "favorites/btn", book: @book %>
            </td>
+           <td id="comment_counter">
+             <%= render "book_comments/counter", book: @book %>
+           </td>
            <% if @book.user == current_user %>
            <td><%= link_to "Edit", edit_book_path(@book), class: "btn btn-sm btn-success" %></td>
            <td><%= link_to "Destroy", @book, method: :delete, data: { confirm: "本当に消しますか?" }, class: "btn btn-sm btn-danger" %></td>
            <% end %>
          </tr>
        </table>
        
+       <div id="comment_index">
+         <%= render "book_comments/index", book: @book %>
+       </div>
        <%= render "book_comments/form", book: @book, book_comment: @book_comment %>
        
      </div>
    </div>
  </div>

IDを要素に追加することで、その要素を独自に特定することができます。JavaScriptのコードでは、IDを使って特定の要素にアクセスして操作することができます。

Controller編集

htmlではなく、jsファイルを読み込ませるため、コントローラを以下の通り編集します。

app/controllers/book_comments_controller.html.erb
  class BookCommentsController < ApplicationController
    def create
      book = Book.find(params[:book_id])
      comment = current_user.book_comments.new(book_comment_params)
      comment.book_id = book.id
      comment.save
-     redirect_to request.referer
    end
  
    def destroy
      BookComment.find(params[:id]).destroy
-     redirect_to request.referer
    end
  
    private
  
    def book_comment_params
      params.require(:book_comment).permit(:comment)
    end
  end

解説:
redirect_toを削除します。非同期通信を行う場合は、JavaScriptファイル(.js.erb)を使用してビューを更新します。アクション内にrenderredirect_toの記述がない場合、Railsは自動的に対応するJavaScriptファイルを探しに行きます。例えば、create.js.erbという名前のファイルがあれば、それが読み込まれます。

js.erbファイル作成

js.erbファイルを作成します。

app/views/book_comments/destroy.js.erb
$('#comment_counter').html("<%= j(render 'book_comments/counter', book: @comment.book) %>");
$('#comment_index').html("<%= j(render 'book_comments/index', book: @comment.book) %>");
app/views/book_comments/create.js.erb
$('#comment_counter').html("<%= j(render 'book_comments/counter', book: @comment.book) %>");
$('#comment_index').html("<%= j(render 'book_comments/index', book: @comment.book) %>");
$('#comment_textarea').val("");

解説:
$('#comment_textarea').val("");は、コメントフォームのテキストエリアの値を空にするためのコードです。

テキストエリアはユーザーがテキストを入力するためのものであり、その入力されたテキストは「値(value)」として表現されます。.val("")は、指定された要素の値を設定するための関数であり、カッコ内には新しい値を指定します。

この場合、コメントフォームのテキストエリアを対象にしています。$('#comment_textarea')は、IDがcomment_textareaである要素を選択するためのセレクタです。.val("")とすることで、テキストエリアの値が空文字列に設定され、すなわちテキストエリアが空になるという動作を意味します。

つまり、このコードを実行することで、コメントの入力欄が空にリセットされ、ユーザーは新しいコメントを入力するために空のフォームから始めることができます。


コメント機能の非同期通信化でした☆

Discussion