4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

railsでコメント投稿機能を非同期化する(jQueryなし)

Posted at

開発環境

Mac OS Catalina 10.15.7
ruby 2.6系
rails 6.0系

前提

同期でのコメント投稿機能は実装済みとする
JavaScriptのフレームワークは使っていません

各テーブルとアソシエーションは以下の通り

users テーブル

Column Type Options
nickname string null: false
email string null: false, unique: true
encrypted_password string null: false

Association

  • has_many :posts
  • has_many :comments
  • has_many :likes

postsテーブル

Column Type Options
title string null: false
explanation text
category_id integer null: false
animal_name string
user references null: false, foreign_key: true

Association

  • belongs_to :user
  • has_many :comments

commentsテーブル

Column Type Options
user references null: false, foreign_key: true
post references null: false, foreign_key: true
content string null: false

Association

  • belongs_to :user
  • belongs_to :post

部分テンプレートに切り出す

まずは差し替えたい部分を切り出します。
自分の場合はコメント部分を切り出す事にしました。
切り出したものはapp/views/commentsのなかに配置しましょう。(commentsディレクトリが無ければ作ってください。)

_comment.html.erb

# このIDは削除する時に使うもので、コメント投稿の非同期化には関係ありません。
<p id="comment_<%= comment.id %>">
  <strong><%= link_to comment.user.nickname, user_path(comment.user.id) ,class: "comment-user"%>:</strong>
  <%= comment.content %>
  <% if user_signed_in? && current_user.id == comment.user.id %>
    <span><%= link_to '[削除する]', post_comment_path(post.id, comment.id), method: :delete, class: "comment-delete", remote: true %></span>
  <% end %>
</p>
show.html.erb
<div class="comment-container">
   <div class = "comment-box">
      <h2>気になった投稿にコメントしよう!</h2>
      <% if user_signed_in? %>
        <%= form_with(model: [@post, @comment], remote: true) do |form| %>
          <%= form.text_area :content, placeholder: "コメントする", rows: "2" %>
          <%= form.submit "コメントを送信する" %>
        <% end %>
      <% else %>
        <strong><p class = "alert">※※※ コメントの投稿には新規登録/ログインが必要です ※※※</p>
        </strong>
      <% end %>
      <div class="comments" id="comments">
        <h4><コメント一覧></h4>
        <% @comments.each do |comment| %>
          # この部分を切り出しました。
          <%= render "comments/comment", post: @post, comment: comment %>
        <% end %>
      </div>
    </div>
 </div>

ポイントは部分テンプレート内で使う変数を渡してあげる事です。

# この部分のこと
post: @post, comment: comment 

form_withをremote: trueにする

次にform_withのlocal: trueをremote: trueに変更します。

変更前

show.html.erb
<%= form_with(model: [@post, @comment], local: true) do |form| %>
  <%= form.text_area :content, placeholder: "コメントする", rows: "2" %>
  <%= form.submit "コメントを送信する" %>
<% end %>

変更後

show.html.erb
<%= form_with(model: [@post, @comment], remote: true) do |form| %>
  <%= form.text_area :content, placeholder: "コメントする", rows: "2" %>
  <%= form.submit "コメントを送信する" %>
<% end %>

これで、コントローラーのcreateアクションのビューの参照先が、create.js.erbに変わりました。(コントローラーはlocal: trueにするとhtml.erbのビューを、remote: trueにするとjs.erbのビューを探しに行きます。)

コントローラーのリダイレクトの記述を削除する

せっかくremote: trueにしてもリダイレクトしてしまうので、削除しましょう。

変更前

comments_controller.rb
class CommentsController < ApplicationController

  def create
    @comment = Comment.create(comment_params)
    redirect_to post_path(params[:post_id])
  end

  def destroy
    @comment = Comment.find(params[:id])
    @comment.destroy
    redirect_to post_path(params[:post_id])
  end

  private
  def comment_params
    params.require(:comment).permit(:content).merge(user_id: current_user.id, post_id: params[:post_id])
  end

end


変更後

comments_controller.rb
class CommentsController < ApplicationController

  def create
    @comment = Comment.create(comment_params)
  end

  def destroy
    @comment = Comment.find(params[:id])
    @comment.destroy
  end

  private
  def comment_params
    params.require(:comment).permit(:content).merge(user_id: current_user.id, post_id: params[:post_id])
  end

end

create.js.erbを編集する

commentsディレクトリにcreate.js.erbファイルを作り、以下のように編集します。

create.js.erb
var element = document.querySelector(".comments")
element.innerHTML += '<%= j(render partial: "comments/comment", locals: {post: @comment.post, comment: @comment}) %>'
document.querySelector("#comment_content").value = ""

コードを説明すると、まず1行目で、コメントの一覧表示がされるcommentsクラスを持つ要素を取得して、elementという変数に代入しています。(詳しくは上記のshow.html.erbを参照してください)

その後、変数elementにinnerHTMLを使い、切り出した_comment.html.erb(コメントの中身の部分)を追加しています。
また、_comment.html.erbでは変数commentと変数postが使われているので、commentsコントローラーで変数@commentに保存したコメントを代入する記述を書き(上記comments_controller.rb参照)、create.js.erbでlocalsオプションを使って、データを渡してあげましょう。

これでコメント投稿の非同期化は完成です。

しかし、このままだと、コメントの投稿フォームに、投稿したコメントが残ってしまうので、最後にコメントの中身を削除するために、投稿フォームをIDで取得して、空にする記述を書いています。

長くなりましたが、以上です。
参考になれば幸いです。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?