[Rails5] submitタグでAjaxを使うと2回目以降に無効になる

# やりたいこと

メッセージを送信するフォームでsubmitを実行した時、AjaxでPOSTを実行させてメッセージ一覧に送信した内容を追加する。

# 環境

Ruby: 2.3.1
Rails: 5.0.7
ブラウザ: Google Chrome
OS: macOS 10

# ハマったこと
以下の通り実装したところ、最初のメッセージの送信は成功するが、2回目以降はsubmitイベントが発火しなくなった。
というか、そもそもsubmitボタンを押せなくなった。

![sent-first-message.jpg](https://qiita-image-store.s3.amazonaws.com/0/249168/cf00a37a-2423-91c4-ffc5-98a2ff219cfc.jpeg)

## ビュー

```haml:index.html.haml
= form_for @message do |f|
  = f.text_field :content, class: "form__text"
  = f.submit "Send", class: "form__submit"
```

↓初回ロード時に出来上がったHTML

```html:index
<form class="new_message" id="new_message" action="/messages" accept-charset="UTF-8" method="post">
 　〜中略〜
 <input class="form__text" type="text" name="message[content]" id="message_content">
 <input type="submit" name="commit" value="Send" class="form__submit" data-disable-with="Send">
</form>
```

ビューのレイアウト
![フォームレイアウト.png](https://qiita-image-store.s3.amazonaws.com/0/249168/d76bd9f1-b75e-a480-be87-ee3f29aa1b77.png)

## JavaScript

JavaScript(jQuery)はこんな感じ。

```js:send.js
$(() => {
  // formタグのsubmitが実行された時の処理
  $("#new_message").on("submit", (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const url = $(e.target).attr("action");

    // Ajax処理
    $.ajax({
      url: url,
      type: "POST",
      data: formData,
      dataType: "json",
      processData: false,
      contentType: false
    }).done((data) => {
      // 成功したらメッセージ表示エリア（class="messages"）に送信したメッセージをHTMLで追加する
      // ここはテンプレートリテラルで自力でHTMLを組み立てる
      const html = `<div class="message__content">
                   ${data.content}
                  </div>`;
      $(".messages").append(html);
      $(".form__text").val("");
    }).fail(() => {
      // 失敗した時の処理
      alert("メッセージの送信に失敗しました。");
    });
  });
});
```

# 直接原因（なぜ2回目以降はイベントが発火しないのか？）

1回目のsubmit後、改めて開発者ツールでHTMLを見てみるとこうなっていた。

```html:index
<form class="new_message" id="new_message" action="messages" accept-charset="UTF-8" method="post">
 <input class="form__text" type="text" name="message[content]" id="message_content">
 <input type="submit" name="commit" value="Send" class="form__submit" data-disable-with="Send" disabled="">
</form>
```

なぜ**disabled**が入ってるんだ！！
disabledのせいでsubmitボタンが無効化され、イベントが発火しなかったのです。

# 間接原因（なぜ勝手にdisabledが入ったのか？）

一度送信すると自動でdisabledが有効になる理由は、「data-disable-with」なる属性がsubmitに埋め込まれているからでした。
これは2重送信を抑止するために、submit_tagにRails5からデフォルトで追加された仕様のようです。

Rails5.0の[リリースノート](https://railsguides.jp/5_0_release_notes.html#action-view-%E4%B8%BB%E3%81%AA%E5%A4%89%E6%9B%B4%E7%82%B9)に書いていました。
>disable_withをsubmitタグのデフォルトの動作に設定。これにより送信時にボタンを無効にし、二重送信を防止する。

# 解決策

仕様で自動的に無効になってしまうようですが、この設定自体を無効化する（Rails4以前の仕様に戻す）ことも可能なようです。
ただ、それだと今回のRails仕様変更で対策した2重送信問題が顕在化してしまうので、イベント処理で再度有効化してあげる方が影響が少なくて済みそうです。

以降にそれぞれの解決方法を記載します。

## その1）Railsの設定を変更する方法

```ruby:config/application.rb
class Application < Rails::Application
  config.action_view.automatically_disable_submit_tag = false # この行を追加
end
```

↓Webサーバ再起動後のHTMLファイル

```html:index
<input type="submit" name="commit" value="Send" class="form__submit">
```

data-disable-withが消えました！

## その2）jQueryでなんとかする

submit実行後に自動でdisabled属性が追加されるので、Ajax通信終了処理としてこの属性を消してしまいましょう。

```js:send.js
　〜中略〜
$.ajax({
　〜中略〜
}).done((data) => {
  const html = buildHTML(data);
  $(".messages").append(html);
  $(".form__text").val("");
}).fail(() => {
  alert("メッセージの送信に失敗しました。");
})
//　ここから
.always(() => {
$(".form__submit").removeAttr("disabled");
});
// ここまで追加
　〜中略〜
```

# 参考

- [Rails5.0からdata-disable-withがデフォルトの動きとなった記載（Railsガイド　5.0リリースノート）](https://railsguides.jp/5_0_release_notes.html#action-view-%E4%B8%BB%E3%81%AA%E5%A4%89%E6%9B%B4%E7%82%B9)

- [Railsの設定で回避する方法（stack overflow: Rails data-disable-with re-enabling button）
](https://stackoverflow.com/questions/42016113/rails-data-disable-with-re-enabling-button)

- [2重送信をするための方法（Qiita: Railsでform２重送信でハマる）](https://qiita.com/otoan/items/7faaf7c3c02ec35ad8f9)

- [テンプレートリテラルについて（Qiita: JavaScript の テンプレートリテラル を極める！）](https://qiita.com/kura07/items/c9fa858870ad56dfec12)

# 近況
最近はiOSアプリ開発にも手を出そうと、XcodeとSwift4と共にヤフオクアプリのエンジニアの登竜門らしい割引計算アプリを作って勉強中。