LoginSignup
0
0

More than 1 year has passed since last update.

【Rails】【Ajax】コメント投稿・削除を非同期通信化する。

Posted at

はじめに

プログラミング初学者の者です。オンラインでプログラミングを学習させて頂いてるのですが、さらに理解を深めていきたいと思い、学習したことを備忘録として、残して理解を深めていきたいと思います。
また、間違った点など、ありましたら、ご指摘いただけると幸いです。よろしくお願いします。

前提

Railsにて投稿アプリケーションを作成のち、投稿に対して、コメントできる機能実装済み。
今回は離乳食を投稿するアプリケーションで実装しています。

目標

コメント機能が非同期で行えるようにする

Image from Gyazo

手順

①jQueryの導入
②コメント表示部分を部分テンプレート化
③投稿機能の非同期化
④削除機能の非同期化

①jQueryの導入

text.Gemfile
---------略---------
gem 'mini_magick'
gem 'image_processing', '~> 1.2'
gem 'pry-rails'
gem 'devise'
gem 'ransack'
gem 'active_hash'
gem 'jquery-rails' //追記する

次にアプリケーションのターミナルで下記コマンドを実行します。

% yarn add jquery

jQueryが追加されているか⌘+Fの検索ボックスを用いて確認します。「jQuery」と入力すると記述されていつファイルが見つかるので
・package.json => "jquery": "^*.*.*"
・yarn.lock => jquery@^*.*.*:
上記、二つにファイルが見つかれば確認完了です。
次に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

application.jsにコードを追記

app/javascript/packs/application.js
require("@rails/ujs").start()
// require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")  //このコードを追記
require("../recipe");

ここまでで、jQueryの導入は完了です。

②コメント表示部分を部分テンプレート化

app/views/recipes/show.html
  <div class="recipe-comments">
----------ここから下----------
      <% @comments.each do |comment| %>
      <div class="comments_list">
        <div class="comments-title">
        <%= comment.user.user_name %>さんの投稿
        </div>
        <div class="comments-items">
         <div class="content">
          <%= comment.content %>
         </div>
         <div class="comment-delete">
          <% if current_user == comment.user %>
           <%=link_to "削除", recipe_comment_path(comment.recipe,comment), method: :delete, class:"comment-delete" %>
          <% end %>
         </div>
        </div>
      </div>
      <% end %>
----------ここまでを切り取る----------
    <div id="comments"><%= render 'comments/comments', comment: @comment %></div>   ///このコードを追記
      <%= form_with model: [@recipe, @comment], local: true do |f| %>
      <div class = 'form-group'>
        <%= f.text_area :content, class: "form-control", id:"comment_content", placeholder: "コメントを記入してください" %>
      </div>
        <%= f.submit "コメントする", class: "btn btn-primary" , id: "submit" %>
      <% end %>
  </div>
</div>
<%= render "shared/footer" %>

views/commentsディレクトリの配下に_comments.html.erbを作成して先ほどの切り取りを貼り付ける

app/views/comments/_comments.html
<% @comments.each do |comment| %>
  <div class="comments_list">
    <div class="comments-title">
      <%= comment.user.user_name %>さんの投稿
    </div>
    <div class="comments-items">
      <div class="content">
        <%= comment.content %>
      </div>
      <div class="comment-delete">
      <% if current_user == comment.user %>
        <%=link_to "削除", recipe_comment_path(comment.recipe,comment), method: :delete, class:"comment-delete" %>
      <% end %>
      </div>
    </div>
  </div>
<% end %>

③投稿機能の非同期化

form_withlocal: trueを外しcreate.js.erbを探しにいくようにする。

show.html
 <div class="recipe-comments">
    <div id="comments"><%= render 'comments/comments', comment: @comment %></div>
      <%= form_with model: [@recipe, @comment] do |f| %>  ///local: trueを外す
      <div class = 'form-group'>
        <%= f.text_area :content, class: "form-control", id:"comment_content", placeholder: "コメントを記入してください" %>
      </div>
        <%= f.submit "コメントする", class: "btn btn-primary" , id: "submit" %>
      <% end %>
  </div>
</div>
<%= render "shared/footer" %>

コントローラーのredirect_toを削除する

comments_controller.rb
 class CommentsController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
  def create
    @recipe = Recipe.find(params[:recipe_id])
    @comments = @recipe.comments.includes(:user)
    @comment = Comment.new(comment_params)
    @comment.user_id = current_user.id
    @comment.recipe_id = @recipe.id
-----ここから削除-----
    if @comment.save
      redirect_to recipe_path(@recipe)
    end
-----ここから削除-----
    @comment.save
  end

  def destroy
    @recipe = Recipe.find(params[:recipe_id])
    @recipe_comments = @recipe.comments
    if Comment.find_by(id: params[:id], recipe_id: params[:recipe_id]).destroy
      redirect_to recipe_path(@recipe)
    end
  end

  private
  def comment_params
    params.require(:comment).permit(:content)
  end
end

views/commentsの配下にcreate.js.erbを作成

create.js
$("#comment_content").val("");
$('#comments').html("<%= escape_javascript(render 'comments', comment: @comment) %>")

ここまでで、コメント投稿機能が非同期通信で行えるようになりました。

④削除機能の非同期化

link_toではremote: trueと指定することでdestroy.js.erbを探しにいくようにします。

comments.html
 <% @comments.each do |comment| %>
  <div class="comments_list">
    <div class="comments-title">
      <%= comment.user.user_name %>さんの投稿
    </div>
    <div class="comments-items">
      <div class="content">
        <%= comment.content %>
      </div>
      <div class="comment-delete">
      <% if current_user == comment.user %>
        <%=link_to "削除", recipe_comment_path(comment.recipe,comment), method: :delete, remote: true, class:"comment-delete" %>///deleteの後にremoteを追加
      <% end %>
      </div>
    </div>
  </div>
<% end %>

destroyアクションのredirect も削除する。

comments_controller.rb
 class CommentsController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
  def create
    @recipe = Recipe.find(params[:recipe_id])
    @comments = @recipe.comments.includes(:user)
    @comment = Comment.new(comment_params)
    @comment.user_id = current_user.id
    @comment.recipe_id = @recipe.id
    @comment.save
  end

  def destroy
    @recipe = Recipe.find(params[:recipe_id])
    @recipe_comments = @recipe.comments
-----ここから下削除-----
    if Comment.find_by(id: params[:id], recipe_id: params[:recipe_id]).destroy
      redirect_to recipe_path(@recipe)
    end
-----ここまで削除-----
-----ここから追記-----
    @comments = @recipe.comments.includes(:user)
    Comment.find_by(id: params[:id], recipe_id: params[:recipe_id]).destroy
-----ここまで追記-----
  end

  private
  def comment_params
    params.require(:comment).permit(:content)
  end
end

views/commnetsの配下にdestroy.js.erbを作成します。

destroy.js
+$('#comments').html("<%= escape_javascript(render 'comments', comment: @comment) %>")

ここまで削除機能も非同期で行えるようになります。
中々、自分だけでは解決できず、Qiitaの記事を参考にさせていただきながら、質問もさせていただきながら実装することができました。もっと理解を深めるため、より詳しく説明できるようしていきたいと思います。

0
0
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
0
0