webエンジニアの日常

RubyやPython, JSなど、IT関連の記事を書いています

remotipartの不思議な挙動

デフォルトではajaxでファイル送信はできないのですが、それを可能にするのが、remotipartというgemです。

ただ単にファイル送信を可能にするだけならgemをインストールするだけで終わりなのですが、レスポンスを受け取ってエラーを表示したいときに少しはまったので解決策を書いておきます。

remotipartのインストール

railsアプリ内で使うことを前提としています。

まず、Gemfileに追加&bundle install します

# ajaxでファイル送信を行うのに必要
gem 'remotipart'

bundle installが終わると、jquery.remotipart.jsというjsもインストールされるので、 vendor/assets/javascripts あたりに置いておきます。

ファイル送信を行う画面が読み込んでいるjsファイル内で、

//= require jquery.remotipart

と書いておきます。

以上でインストールは終わりです。

remotipartを使ったときの不思議な挙動

1. xhr.responseJSONが取れない

$("form").on('ajax:error', function(event, xhr, status, error) {
  console.log(xhr.responseJSON);
}

とすれば、エラーメッセージが配列で表示されるはず(バリデーションエラーなどでは、そのようなjsonを返している)なのですが、なぜか、ファイルを送った場合だけjsonがxhr.responseJSONが取れなかったです。

解決策

form_forのオプションに data: {type: :json}を追加することで解決できました。

サーバのログを見ていると、ファイルがあるときは、レスポンスフォーマットがjsになっていました。何も指定しないとそうなるようです。

2. ‘ajax:success'が発火しない

上記の解決策をし、サーバー側で以下のような処理をしていると起こります。

  def update
    if @user.update(update_params)
      respond_to do |format|
        format.json { head :no_content, status: :ok }
      end
    end
  end

画面側では、

<script type="text/javascript">
  $("form")
    .on('ajax:success', function() {
      location.reload();
    })
    .on('ajax:error', function(event, xhr, status, error) {
      console.log(xhr.responseJSON);
    });
</script>

としていました。

ですが、ファイルを送信した場合だけ、なぜか'ajax:success'が発火しないです。ファイルを選択しない場合は問題なく動作しました。

解決策

問題はhead no_contentの部分です。

ここを、

  def update
    if @user.update(update_params)
      respond_to do |format|
        format.json { render json: @user, status: :ok }
      end
    end
  end

のように、何か値を送ると、ちゃんと発火するようになりました。