Rails つまみぐい

Rails つまみぐい

Ruby on Rails 初学者による行き当たりばったりなメモ

Railsでajaxリクエストが二重に送信されてしまう

Ruby on Rails v3.2.11
jquery-rails v2.0.1


フォームからajax送信する際に、送信ボタンを2回押しているわけではないのですが、なぜか毎回2回リクエストが送信されてしまいます。ajax送信は以下のような感じ(一部抜粋)でやっています。

<%= form_tag(:action => :scan, 
             :data-remote => true,
             :data-type => :html,
             :id => "my_form")  do %>
    <%= text_field_tag 'name'  %>
    <%= submit_tag "submit" %>
<%- end -%>

def scan
    render :layout => false
end

$(function(){
    $("my_form").on('ajax:success', function(data, status, xhr) {
        $("#result_area").html(status);
        alert("Finish!");    //テスト用
    });
});

こんな感じでやってますが、alertのウィンドウが2回表示されてしまいます。今回は参照のリクエストなのであまり影響はないですがどうも気持ち悪い。

原因は?

ウェブで同じような状況を探してみると、アセットパイプラインの取り扱いによって(?) 同じjavascriptファイルが二重に読み込まれてしまうケースがある、それによって1つの要素に対して.bind系(.live とか .on とか .delegate)が二重にセットされてしまう、ということが原因のようです。

自分のコードを見直してみて、application.jsの中の //= require_tree . を消して1つ1つ直接名前で指定したり、public/assetsを消したり(developmentモード)と色々やってみたのですが、ダメ…(x_x)

(問題の箇所をうまく見つける方法が分かりません。誰か教えてください。)


むりくり対策

よく分からないので、とりあえず二重にリクエストが飛んだときに2回目の処理をキャンセルするという、いささか強引な方法で対策してみました。

  • 1回目のajax:beforeSendが送信された時に、form要素の属性として'ajax-processing'(名前はなんでもいいです)をくっつけます。
  • 2回目のajax:beforeSendではform要素の'ajax-processing'属性をみてreturn false;して以降の処理をキャンセルします。
  • 処理終了後にajax:completeが送信された際に'ajax-processing'属性を取り除きます。

$(function(){
    $("form[data-remote=true]").on( 'ajax:success',
        function(data, status, xhr) {
            if ($(this).attr('ajax-processing')) {
                return false;
            } else {
                $(this).attr('ajax-loading', true);
            }
        }
    );

    $("form[data-remote=true]").on('ajax:complete',
        function() {
            $(this).removeAttr('ajax-processing');
        }
    );
});


あまり根本的な解決でないところがすっきりしませんが、1回しか処理されなくなったので一応よしとしておきます。