73
69

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

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

Last updated at Posted at 2018-04-23

やりたいこと

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

環境

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

ハマったこと

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

sent-first-message.jpg

ビュー

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

↓初回ロード時に出来上がった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

JavaScript

JavaScript(jQuery)はこんな感じ。

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を見てみるとこうなっていた。

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のリリースノートに書いていました。

disable_withをsubmitタグのデフォルトの動作に設定。これにより送信時にボタンを無効にし、二重送信を防止する。

解決策

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

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

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

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

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

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

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

その2)jQueryでなんとかする

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

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

参考

近況

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

73
69
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
73
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?