【Rails】コメント機能(非同期化通信)
非同期通信を用いてコメント機能を実装します。
別記事にて実装したいいね機能と合わせてみるとわかりやすいかもです。
実装する機能
- コメント一覧を部分テンプレートすること
- サーバーからのレスポンスでコメント一覧のテンプレートを返すこと
- コメントの投稿後、コメントフォームの値に前回の投稿が残らないようにしましょう
- form_withを使うこと
jQueryをインストール
gem 'jquery-rails'
Gemfileに上記を記述し、JavascriptのライブラリであるjQueryをインストールします。ターミナルにて「bundle install」を忘れずに!
View作成
非同期化させるところを部分テンプレート化させる
今回の場合は以下の3つに分けて部分テンプレートを作成
- コメント数
コメント数: <%= book.book_comments.count %>
- コメント一覧
<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. コメント投稿用フォーム
<%= 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_to
のremote: true
と同じで、非同期通信を処理するために
fonm_with
ではlocal: false
を指定します。
またid: "comment_textarea"
を追加してcomment_textarea
というというIDがテキストエリアに割り当てられるようにします。
idを追加
・
・
<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のリダイレクト先を削除
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
を削除することによりアクション内にrender
やredirect_to
の記述がない場合、Railsは自動的に対応するJavaScriptファイルを探しに行きます。
jsファイルの作成
$('#comment_counter').html("<%= j(render 'book_comments/counter', book: @book) %>");
$('#comment_index').html("<%= j(render 'book_comments/index', book: @book) %>");
$('#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_with
にlocal: false
の記入がされていなかったことが原因でした。そのため、非同期化通信がされておらず~ as HTML
と表記されていたのでした。
非同期化通信ではエラーはターミナル上にしか出ないため、エラーが出た時点でまずは非同期化ができていないこと、それに関する内容を確認することを学んだトラブルでした。
ちなみに非同期化通信がされている状態でのエラーは下記のように普通のエラーと同じ記載がターミナル上に発生するので確認して対応をしていきましょう。
参考ページ
Discussion