🎤

コメント機能(非同期通信)

2023/10/05に公開2

先日行った、いいね機能の非同期通信に引き続き、
コメント機能も同じように行っていきます。基本的な手順は似ているので、
jQueryのインストールやAjaxの概要は省略します。下記の参照をお願いいたします。

https://zenn.dev/yayu1303/articles/6dcf1cd5076dbe

非同期通信化する場所

alt完成図

  • コメント数の更新
  • 送信されたコメント
  • 送信されたコメントが残らないようする

完成図のコード全体像

ページの構成はこのようになっております。

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: @newbook %>
   </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:"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>コメント数:<span id="comment_count_<%= @book.id %>"><%= @book.book_comments.count %></span></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, book_comment: @book_comment %>
     </div>
     <div id="comment_form">
       <%= render 'book_comments/form', book: @book, book_comment: @book_comment %>
     </div>
   </div>
 </div>
</div>

非同期通信に必要な部分の確認

  • コメント数の更新
<td>コメント数:<span id="comment_count_<%= @book.id %>"><%= @book.book_comments.count %></span></td>
  • 送信されたコメント
<div id="comment_index">
 <%= render 'book_comments/index', book: @book, book_comment: @book_comment %>
</div>
  • 送信されたコメントが残らないようする
<div id="comment_form">
  <%= render 'book_comments/form', book: @book, book_comment: @book_comment %>
</div>

以降、順番にコードの解説をしていきます。

コメント数の更新

<td>コメント数:<span id="comment_count_<%= @book.id %>"><%= @book.book_comments.count %></span></td>

<%= @book.book_comments.count %>にHTMLのidでcomment_count_<%= @book.id %>を付与するためにspanタグで囲みます。idの付与は後にjsファイルを編集する際に必要になります。

送信されたコメント

部分テンプレートの全体像を確認します。

books/_index.html.erb
<div class="d-inline-flex flex-column">

  <% book.book_comments.each do |book_comment| %>
    <div class="d-flex">
      <div class="mb-3">
        <p><%= image_tag book_comment.user.get_profile_image, size:"70x70" %></p>
        <%= link_to book_comment.user.name, user_path(book_comment.user) %>
      </div>
      <div class="ml-3">
        <%= book_comment.created_at.strftime('%Y/%m/%d') %>
        <%= book_comment.comment %>
        <% if book_comment.user == current_user %>
          <%= link_to "削除", book_book_comment_path(book_comment.book, book_comment),
          class: "btn btn-sm btn-danger", method: :delete, remote:true %>
        <% end %>
      </div>
    </div>
  <% end %>

</div>

非同期通信に関連する箇所

<%= link_to "削除", book_book_comment_path(book_comment.book, book_comment),
          class: "btn btn-sm btn-danger", method: :delete, remote:true %>

link_toremote:trueを定義することで、非同期通信に対応させます。

books/show.html.erb
<div id="comment_index">
	<%= render 'book_comments/index', book: @book, book_comment: @book_comment %>
</div>

冒頭に確認した部分テンプレートを記述したファイルに戻ります。
こちらでも、jsファイルに記述するためのHTMLのidを付与します。

送信されたコメントが残らないようする

部分テンプレートの全体像を確認します。

<div>
  <%= form_with model:[book,book_comment], local: false do |f| %>
    <%= f.text_area :comment, rows: '5', cols:'100%', id:"comment_area" %>
    <%= f.submit "送信" %>
  <% end %>
</div>

非同期通信に関連する箇所

  1. form_withの中にlocal: falseを記述します。
    こちらも非同期通信に対応させるための記述です。
  2. ユーザーがコメントを入力する部分のf.text_areaにHTMLのidを付与します。

コントローラーの変更

book_comments_controller.rb
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
    @book = Book.find(params[:book_id])
    # redirect_to request.referer
  end

  private

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

end

jsファイルへの遷移をするため、redirect_toの削除を行います。

jsファイルの作成・記述

books/destroy.js.erb
$('#comment_index').html("<%= j(render 'book_comments/index', book: @book, book_comment: @book_comment) %>");
$('#comment_count_<%= @book.id %>').html("<%= @book.book_comments.count %>");
books/create.js.erb
$('#comment_index').html("<%= j(render 'book_comments/index', book: @book, book_comment: @book_comment) %>");
$('#comment_count_<%= @book.id %>').html("<%= @book.book_comments.count %>");
$('#comment_area').val("");

destroy.js.erbcreate.js.erbを新たに作成し、上記の記述を行います。
冒頭で書いた「送信されたコメントが残らないようする」のはcreate.js.erb'$('#comment_area').val("");'と記述し、ユーザーが入力後のテキストエリアに空の値を取得することで実現します。


【おまけ】本日のNG集

よし、コードの記述完了!
動かしてみよう・・・あれ、リロードしないと投稿コメントとコメント数が反映されないぞ?
ターミナルを見てみよう。

ターミナル
ActionView::Template::Error (undefined method `book_comments' for nil:NilClass):
    1: <% book.book_comments.each do |book_comment| %>
    2:   <p><%= image_tag book_comment.user.get_profile_image, size:"100x100" %></p>
    3:   <%= book_comment.user.name %>
    4:   <%= book_comment.created_at.strftime('%Y/%m/%d') %><%= book_comment.comment %>

んーー、bookが読み取れてなさそうだな・・・どうしてだろ?


(3時間後)


あ、renderしてるんだからコメントコントローラーで@bookにすればいいんだ(チーン)
気付いたあとは30秒足らずで解決しました(´;ω;`)

参考にした皆様

Airichanさん
がんもさん
日々、大変参考になる記事の作成をありがとうございます!

Discussion

AirichanAirichan

お疲れ様です!(^^)
参考になっていたらとても嬉しいです♡(私も過去の私にありがとうしますw)
アウトプット、大変だと思いますが必ず力になるのでファイトです🌟
(仕事し始めてから、過去の自分の記事見るのも楽しかったりします笑)
すぎさん、応援しています!

すぎすぎ

お返事いただきありがとうございます!
すばらしい財産を残してくださり、ありがとうございます!
なかなか、思い悩むことも多い記事作成ですが、楽しんで続けていけたらいいなと思います。
応援コメント、本当にうれしいです。
卒業まで、少しずつでもアウトプット頑張ります!
エンジニアになって過去の自分をよしよししてあげれたらと思います(笑)