🔥

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

2023/10/05に公開

非同期通信を用いてコメント機能を実装します。
別記事にて実装したいいね機能と合わせてみるとわかりやすいかもです。
https://zenn.dev/sudoukky/articles/551bf112c47109

実装する機能

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

jQueryをインストール

Gemfile.
gem 'jquery-rails'

Gemfileに上記を記述し、JavascriptのライブラリであるjQueryをインストールします。ターミナルにて「bundle install」を忘れずに!

View作成

非同期化させるところを部分テンプレート化させる

今回の場合は以下の3つに分けて部分テンプレートを作成

  1. コメント数
views/book_comments/_counter.html.erb
コメント数: <%= book.book_comments.count %>
  1. コメント一覧
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はlink_toに加えることで自動的に Ajax での処理にすることができます。
そのためページがリロードされることなく変更を反映することができます。
3. コメント投稿用フォーム

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

コメント一覧時に使用したlink_toremote: trueと同じで、非同期通信を処理するために
fonm_withではlocal: falseを指定します。
またid: "comment_textarea"を追加してcomment_textareaというというIDがテキストエリアに割り当てられるようにします。

idを追加

views/books/show.html.erb
・
・
<h2>Book detail</h2>
<table class='table'>
 <tr>
   <td><%= link_to(@book.user) do %>
       <%= image_tag @book.user.get_profile_image, size:"100x100" %><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 %>
    ・
    ・

JavaScriptのコードではIDを使用し特定の要素にアクセスし操作します。

Controllerのリダイレクト先を削除

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
    @book_comment = BookComment.new
    # redirect_to request.referer
  end

  def show
    @book = Book.find(params[:id])
    @book_comment = BookComment.new
  end

  def destroy
    BookComment.find(params[:id]).destroy
    @book = Book.find(params[:book_id])
    # redirect_to book_path(params[:book_id])
  end

  private

  def book_comment_params
    params.require(:book_comment).permit(:comment)
  end

end

非同期通信を行う場合は、JavaScriptファイル(.js.erb)を使用してビューを更新します。そのため、redirect_toを削除することによりアクション内にrenderredirect_toの記述がない場合、Railsは自動的に対応するJavaScriptファイルを探しに行きます。

jsファイルの作成

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

上記はjQueryの記述に記述になります。

  // 対象の子要素のhtmlを、カッコ内のものに変更する。
  $(セレクタ).html();
  // 対象のvalueを、カッコ内のものに変更する。
  $(セレクタ).val();

以上が非同期化通信のコメント機能になります。

作成中のトラブル

今回僕が作成中に起きたエラーを紹介します。

上記工程を一通り行い、アプリケーションを動かした際にエラーが出ました。
「ActionController::UnknownFormat in BookCommentsController#create」

js.erbへ渡す値とコントローラーの関係についてか?と思ったのですが、エラーが出ているのでそもそも非同期化通信されていないのでは?ということでターミナルを確認したところ、、、

ターミナルの表記が「Processing by ~ as HTML」となってとなっていました。

非同期化通信ができていれば「Processing by ~ as JS」と表示されます。
確認したところコメント投稿用フォームの部分テンプレート内でform_withlocal: falseの記入がされていなかったことが原因でした。そのため、非同期化通信がされておらず~ as HTMLと表記されていたのでした。

非同期化通信ではエラーはターミナル上にしか出ないため、エラーが出た時点でまずは非同期化ができていないこと、それに関する内容を確認することを学んだトラブルでした。

ちなみに非同期化通信がされている状態でのエラーは下記のように普通のエラーと同じ記載がターミナル上に発生するので確認して対応をしていきましょう。

参考ページ

https://zenn.dev/ganmo3/articles/ec9f8e166e223d
https://zenn.dev/goldsaya/articles/968b2441342357
https://zenn.dev/airiswim/articles/2b8a49512614da

Discussion