ajax haml JavaScript jQuery rails Ruby

【jQuery】【rails】ajaxでコメント投稿の非同期機能を実装する

2020年8月23日


メル○リのようなフリマアプリ制作にて、商品に対してコメント投稿後に非同期で即時にブラウザに反映されるように実装を行いました。その手順について解説します。コメント投稿の度にリロードが走る同期通信の実装は完了しているものとします。

完成画像

実装手順

以下の手順で実装していきます。
❶ 事前準備
❷ フォームが送信されたら、イベント発火するようにする
❸ 非同期でコメントが保存されるようにする
❹ respond_toを使用してHTMLとJSONの場合で処理を分ける
❺ jbuilderを使用して、作成したメッセージをJSON形式で返す
❻ 返ってきたJSONをdoneメソッドで受け取り、HTMLを作成する
❼ エラー時の処理を行う

❶ 事前準備

Gemにjquery-railsをインストール

gem 'jquery-rails'

application.jsの記述を確認

下記2行が記述されていれば正になります。


//= require jquery
//= require jquery_ujs

必要なクラス名とIDを与える

ajaxによる非同期通信に必要な以下のクラス名、IDをコメントフォームのビューに与えておきます。
id : new_comment
class : textbox
class : form__submit

app/views/items/show.html.haml

.itemcomment__box__content
  - if current_user
    = form_with(model:[@item, @comment], class: "message-form", id: "new_comment") do |form|
      %p 相手のことを考え丁寧なコメントを心がけましょう。不快な言葉遣いなどは利用制限や退会処分となることがあります。
      = form.text_area :text, {class: "textbox"}
      %input{type:"submit", class:"form__submit", value:"コメントする"}
  - else
    %strong
      %p ※※※ コメントの投稿には新規登録/ログインが必要です ※※※

  .itemcomment__box__content__comment
    %p < コメント一覧 >
    - if @comments
      .itemcomment__box__content__comment__member
      - @comments.each do |comment|
        .itemcomment__box__content__comment__member__list
          = comment.user.nickname
          %strong :
          = comment.text

❷ フォームが送信されたら、イベント発火するようにする

jQueryを記述するためのファイルcomment.jsを作成します。コメント投稿ボタンを押したときにイベントが発火するようにします。

app/assets/javascripts/comment.js

$(function(){
  $('#new_comment').on('submit', function(e){
    e.preventDefault();
    var formData = new FormData(this);
  })
})

・ form要素のid属性「new_comment」に対して、onメソッドでイベントを設定します
・ フォーム送信時のデフォルト動作はキャンセルします「e.preventDefault()」
・ FormDataでフォームのデータを取得します

❸ 非同期でコメントが保存されるようにする

フォームの送信が行われたときに、Ajaxによる非同期通信でcommentコントローラのcreateアクションを実行します。

app/assets/javascripts/comment.js

$(function (){
  $('#new_comment').on('submit', function(e){
    e.preventDefault();
    var formData = new FormData(this);
    var url = $(this).attr('action');
    $.ajax({
      url: url,
      type: "POST",
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
  })
})

formDataから取得したフォームの入力データからObjectを作成してthisに代入します。
thisの送り先をurlで指定します。attrで指定した要素のaction属性の値を返します。

❹ コメントを保存して、respond_toを使用してHTMLとJSONの場合で処理を分ける

app/controllers/comments_controller.rb

class CommentsController < ApplicationController
  def create
    @item = Item.find(params[:item_id])
    @comment = @item.comments.build(comment_params)
    @comment = Comment.new(comment_params)
    if @comment.save
      respond_to do |format|
        format.html {redirect_to item_path(@item)}
        format.json
      end
    else
      flash.now[:alert] = 'コメントを入力してください。'
    end
  end

ローカル変数はこの後のjbuilderで使用できないので、インスタンス変数@commentを使用します。

❺ jbuilderを使用して、作成したメッセージをJSON形式で返す

app/views/comments/create.json.jbuilder

json.text @comment.text
json.user_id @comment.user.id
json.user_name @comment.user.nickname

ここでuser_nameを定義していなかったので、非同期通信ではユーザ名がundefinedになりました。

❻ 返ってきたJSONをdoneメソッドで受け取り、HTMLを作成する

app/assets/javascripts/comment.js

$(function (){
  function buildHTML(comment){
    var html = 
      `<div class="itemcomment__box__content__comment__member__list">
           ${comment.user_name}
           : 
           ${comment.text}
        </div>`
    return html;
  }
  $('#new_comment').on('submit', function(e){
    e.preventDefault();
    var formData = new FormData(this);
    var url = $(this).attr('action');
    $.ajax({
      url: url,
      type: "POST",
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
    .done(function(data){
      var html = buildHTML(data);
      $('.itemcomment__box__content__comment__member').prepend(html)
      $('.textbox').val('');
      $('.form__submit').prop('disabled', false);
    })
    .fail(function(){
      alert('error');
    })
  })
})

doneメソッドで非同期通信に成功したパターンを記述します。
buildHTMLで非同期でコメント一覧に追加していくコメントのHTMLを作成します。
2行目からのfunction buildHTMLでは実際のDOMと同じ構成にして、追加するコメントのテンプレートを作成します。(テンプレートリテラル記法)

prependで追加していくクラス名も実際のビューと同じ名前にします。

❼ エラー時の処理を行う

app/assets/javascripts/comment.js

    .fail(function(){
      alert('error');
    })

CATEGORIES & TAGS

ajax, haml, JavaScript, jQuery, rails, Ruby,

Author:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

むるし

インフラ系エンジニア。備忘録で色々書いていきます。
現在テックキャンプでフルコミット中。

年収訴求

CodeCamp

縛りなしWiFi

%d人のブロガーが「いいね」をつけました。