2
1

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 5 years have passed since last update.

ajaxの書き方〜.on('click')の罠〜

Posted at

非同期通信を利用したアプリを作成しましたが
データが2重送信される事案が発生して、何時間も時間を無駄にしたので、その備忘録として。

1 rails newからGemfileの設定

$ rails new ajax-sample
Gemfile
gem 'jquery-rails'
gem 'jbuilder', '~> 2.5'

この二つを用意して

$ bundle install

これで、準備完了です。

2 MVCファイルの準備

今回は、コメントを入力して、表示するアプリを作成してみます。

$ rails g model Comment text:string
$ rails db:migrate
$ rails g controller Comments index
routes.rb
Rails.application.routes.draw do
  root to: 'comments#index'
  resources :comments, only: [:create]
end
comment.rb
class Comment < ApplicationRecord
  validates :text, presence: true
end
comments_controller.rb
class CommentsController < ApplicationController

  def index
    @comments = Comment.all
  end

  def create
    binding.pry
    @comment = Comment.create(comment_params)
    respond_to do |format|
      format.json
    end
  end

  private

  def comment_params
    params.require(:comment).permit(:text)
  end

end
Prefix   Verb   URI Pattern           Controller#Action 
root     GET    /                     comments#index    
comments POST   /comments(.:format)   comments#create                                                                
index.html.erb
<h1>Enter comment</h1>
<%= form_with model: Comment.new,url: :comments, class: "form" do |f| %>
  <%= f.label "comment" %>
  <%= f.text_field :text, class: "text" %>
  <%= f.submit class: "button" %>
<% end %>
<ul>
  <% @comments.each do |comment| %>
    <li><%= comment.id %> <%= comment.text %></li>
  <% end %>
</ul>

このままでは、コメントを作成するたびに、リダイレクトが発生して、読み込み待ちがおきてしまうため、非同期通信を導入してみましょう。

3 非同期通信の設定

非同期通信の流れとしては
送信ボタンをクリック→通常createアクションにデータが送信されるところをストップさせて、jsonデータのやりとりのみにする→データベースからjsonを引っ張ってきて、HTMLに追加する
といった流れです。

まずはapp/assets/javascriptsのapplication.jsに以下の記述があるか確認して、なければ追加します

application.js
//= require jquery
//= require rails-ujs

次に、app/assets/javascriptsにcomment.jsを作成します。

comment.js
$(function() {
  $('.form').on('submit', function(){
    var formData = new FormData(this);
    var href = $('.form').attr('action');
    $.ajax({
      type: 'POST',
      url: href,
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })

これで、投稿したデータをjsonファイルとして、createアクションに送ることができます。

[5] pry(#<CommentsController>)> params
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"fYbMbCkLGkIudntFybf8hgGaVwGm68ZDyaFD0R5O+5EeJ+9iSB65a2NYhJcBgbcISCpmRV036KMdsPQRJ1J1Nw==", "comment"=>{"text"=>"Adele"}, "controller"=>"comments", "action"=>"create"} permitted: false>

[6] pry(#<CommentsController>)> params[:comment]
=> <ActionController::Parameters {"text"=>"Adele"} permitted: false>

formDataを取得して、createコントローラーにjsonファイルとして、データを送信しています。
先ほど作成した、comments_controller.rbでは、jsonファイルを受け取った時には、jsファイルにデータを送るように設定しています。

comments_controller.rb
  def create
    binding.pry
    @comment = Comment.create(comment_params)
    respond_to do |format|
      format.json
    end
  end

次は、jbuilderをインストールしたので、インスタンス変数をjsファイルで使えるようにします。

app/viewsにcreate.json.jbuilderファイルを作成します。(jsonファイルを送ったアクション名を記述し、コントローラー名と同じviewフォルダに作成します。)

create.json.jbuilder
json.text @comment.text
json.id @comment.id

これで、jsファイルでは、@comment.textをcomment.textとして、記述することが可能になりました。

最後に、comment.jsのajax以降を以下のように記述します。

comment.js
$(function() {
  $('.form').on('submit', function(){
    var formData = new FormData(this);
    var href = $('.form').attr('action');
    $.ajax({
      type: 'POST',
      url: href,
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
    .done(function(comment){
      var html = `<li>${comment.id} ${comment.text}</li>`
      $('ul').append(html);
    })
    .fail(function(){
      alert("Enter text !!!");
      $('.text').val('');
    })
    .always(function(){
      $('.form')[0].reset();
      $('.button').removeAttr('data-disabele-with');
    })
    return false; //.on('submit')の時はreturn false;でデータの2重送信を防ぐこと!!
  });
});

これで、簡単な非同期通信を作成することができました。

ちなみに、送信ボタンをクリックしてイベントを発生させる場合(.on('click')の場合)は

comment.js
$('.button').on('click', function(e){
  e.preventDefault();

以上の記述でOKであり、return falseは必要ありません。
作者はここで数時間の時間を費やしてしまいました。。。。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?