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

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

#1 rails newからGemfileの設定
```
$ rails new ajax-sample
```

```ruby:Gemfile
gem 'jquery-rails'
gem 'jbuilder', '~> 2.5'
```

この二つを用意して

```terminal
$ 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
```

```terminal
Prefix   Verb   URI Pattern           Controller#Action 
root     GET    /                     comments#index    
comments POST   /comments(.:format)   comments#create                                                                
```

```html: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に以下の記述があるか確認して、なければ追加します

```javascript: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アクションに送ることができます。

```terminal
[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フォルダに作成します。）

```ruby: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;でデータの２重送信を防ぐこと！！
  });
});
```
これで、簡単な非同期通信を作成することができました。

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

```javascript:comment.js
$('.button').on('click', function(e){
  e.preventDefault();
```
以上の記述でOKであり、return falseは必要ありません。
作者はここで数時間の時間を費やしてしまいました。。。。