🎃

Railsコメント機能の非同期通信化(Ajax)

2023/08/02に公開

前回の記事の続き

https://zenn.dev/airiin/articles/352f1057068633

開発環境

ruby 3.1.2p20
Rails 6.1.7.4
Cloud9

前提

・Userモデル、Bookモデル、BookCommentモデルは作成済み
・Devise導入済み

step1 jQueryの導入

まずはjQueryを読み込みます。

$ yarn add jquery

上記コマンドを実施します。

次に、config/webpack/environment.js に以下のコードを記述します。

config/webpack/environment.js
const { environment } = require('@rails/webpacker')

// 追加ここから
const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery'
  })
)
// 追加ここまで

module.exports = environment

最後に、app/javascript/packs/application.js に以下を追記します。

app/javascript/packs/application.js
require("jquery")  //このコードを追加

これで準備が整いました。

step2 コメント部分のテンプレート化

今回テンプレート化が必要な部分(非同期通信で更新したい部分)は
・コメント一覧表示(book_comments/_index.html.erb)
・コメント送信フォーム(book_comments/_form.html.erb)
・コメント数表示(book_comments/_counter.html.erb)
の3か所です。

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: "100x100" %><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, remote: true, data: {"turbolinks" => false}, class: "btn btn-danger pull-right" %> #ココ
          <% end %>
        </td>
      </tr>
      <% end %>
  </tbody>
</table>

コメント削除機能の Destroyボタンに、
remote: true, data: {"turbolinks" => false}
を追記します。

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 comment-textarea" %> #ココ
  <%= f.submit "送信する", class: "btn btn-lg btn-base-1 mt-20 pull-right btn-secondary" %>
<% end %>

※form_with 部分に local:false を追記します。
なぜかは全くわからないのですが、form_with側にはremote: true, data: {"turbolinks" => false}と記述しても、非同期通信されません。必ずlocal:falseを記述する必要があります。
誰か、理由がわかったら教えてください...😂

また、f.text_area 部分に comment-textarea クラスを追記します。
このクラスは後にjsファイル内で使用します。

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

step3 テンプレート読み込み

次に、step2 で作成したテンプレートを読み込みます。
読み込み部分に idを付与するのを忘れないようにします。
この idは後にjsファイルで使用します。

books/show.html.erb
<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="book-comments-counter"> #ココ
            <%= render 'book_comments/counter', book: @book %> #ココ
          </td>

          <td id="favorite_buttons_<%= @book.id %>">
            <%= render "favorites/btn", book: @book %>
          </td>
          <% if @book.user == current_user %>
            <td><%= link_to 'Edit', edit_book_path(@book), class: "btn btn-sm btn-success edit_book_#{@book.id}" %></td>
            <td><%= link_to 'Destroy', @book, method: :delete, data: { confirm: '本当に消しますか?' }, class: "btn btn-sm btn-danger destroy_book_#{@book.id}"%></td>
          <% end %>
        </tr>
      </table>

      <div id="book-comments-index"> #ココ
        <%= render "book_comments/index", book: @book %> #ココ
      </div>

        <%= render "book_comments/form", book: @book, book_comment: @book_comment %> #ココ

    </div>

編集したのは、#ココ と書いてある3か所です。

step4 コントローラの編集

次に、book_comments_controller を編集します。

book_comments_controller.rb
  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
    @comment = BookComment.find_by(id: params[:id], book_id: params[:book_id])
    @comment.destroy
    # redirect_to request.referer
  end

リダイレクトを削除しました。
このおかげで、create / destroyの処理後、jsファイルを参照してくれます。

また、commentを @commentに変更しました。
後にjsファイルで使用するためです。

step5 jsファイルの作成

create.js.rb
destroy.js.rb を作成します。

views/book_comments/create.js.erb
$('#book-comments-counter').html("<%= j( render 'book_comments/counter', book: @comment.book ) %>")
$('#book-comments-index').html("<%= j(render "book_comments/index", book: @comment.book
$('.comment-textarea').val("");
view/book_comments/destroy.rb
$('#book-comments-counter').html("<%= j(render 'book_comments/counter', book: @comment.book) %>")
$('#book-comments-index').html("<%= j(render 'book_comments/index', book: @comment.book) %>")

参照

https://zenn.dev/goldsaya/articles/968b2441342357
https://qiita.com/ki_87/items/d196bda125cbed88ea13

Discussion