Rails 3:Ajax呼び出しで「redirect_to」する方法は?


85

次のattempt_loginメソッドは、ログインフォームが送信された後にAjaxを使用して呼び出されます。

class AccessController < ApplicationController
  [...]
  def attempt_login
    authorized_user = User.authenticate(params[:username], params[:password])

    if authorized_user
      session[:user_id] = authorized_user.id
      session[:username] = authorized_user.username
      flash[:notice] = "Hello #{authorized_user.name}."
      redirect_to(:controller => 'jobs', :action => 'index')
    else
      [...]
    end
  end
end

問題はそれredirect_toが機能しないことです。

これをどのように解決しますか?

回答:


102

最後に、私はちょうど交換しました

redirect_to(:controller => 'jobs', :action => 'index')

これとともに:

render :js => "window.location = '/jobs/index'"

そしてそれはうまくいきます!


43
より良いアプローチはrender :js => "window.location = '#{jobs_path}'"
次のとおり

3
それは機能しますが、実際のjson成功メッセージでリダイレクト場所を返し、フロントエンドでリダイレクトを行う方がはるかに良いのではないでしょうか?
justinxreese 2014

1
jobs_path基本的にURLほど厳格ではありませんか?特に注意しない限り、URLが変更されると、ルートの名前も変更されます。もう1つの方法はrender js: "window.location = '#{polymorphic_path(@job.class)}'"、ジョブモデルに基づいて計算されたリソースフルルートを使用することです。これは、ルートにリソースがあり、モデルに沿った標準の命名規則を使用している場合にのみ機能します。(または、モデルにmodel_nameを指定して、正しいルート名を生成する場合。)
2014年

2
驚くばかり。単純なredirect_toが機能しない理由を誰かが知っていますか?
Tasos Anesiadis 2016年

1
@Tasos Anesiadis、フォームが「リモート」Railsフォームの場合、ブラウザーはコントローラーからの応答をJavascriptとして解釈するように指示されているため、redirect_toは機能しません。Chrome DevToolsの[応答]タブ([ネットワーク]パネル経由)にredirect_toページが表示されますが、代わりに必要なのは、コントローラーからブラウザーに別のページを検索するように指示することです。ここで提供されるwindow.locationソリューション、またはフォームを通常の「ローカル」フォームに変更する必要があります。ただし、fetch()およびJSONを介してフォームデータを手動で送信および処理する場合を除きます。
MSC

67

次のリクエストのためにフラッシュを保持する非常に簡単な方法があります。コントローラで次のようなことを行います

flash[:notice] = 'Your work was awesome! A unicorn is born!'
flash.keep(:notice)
render js: "window.location = '#{root_path}'"

flash.keep必ずフラッシュが次の要求のために保持されるようになります。したがって、root_pathがレンダリングされると、指定されたフラッシュメッセージが表示されます。Railsは素晴らしいです:)


28

私はこれが少し良いと思います:

render js: "window.location.pathname='#{jobs_path}'"


12
少しだけ良い:render js: "window.location.pathname = #{jobs_path.to_json}"
トークランド2012

26

私のアプリの1つでは、JSONを使用してリダイレクトとフラッシュメッセージデータを実行しています。次のようになります。

class AccessController < ApplicationController
  ...
  def attempt_login
    ...
    if authorized_user
      if request.xhr?
        render :json => {
          :location => url_for(:controller => 'jobs', :action => 'index'),
          :flash => {:notice => "Hello #{authorized_user.name}."}
        }
      else
        redirect_to(:controller => 'jobs', :action => 'index')
      end
    else
      # Render login screen with 422 error code
      render :login, :status => :unprocessable_entity
    end
  end
end

そして、単純なjQueryの例は次のようになります。

$.ajax({
  ...
  type: 'json',
  success: functon(data) {
    data = $.parseJSON(data);
    if (data.location) {
      window.location.href = data.location;
    }
    if (data.flash && data.flash.notice) {
      // Maybe display flash message, etc.
    }
  },
  error: function() {
    // If login fails, sending 422 error code sends you here.
  }
})

1
ここにはたくさんの良い情報があります。render:location、:statusオプション、およびxhrの適切かつ適切な使用?小切手。より多くのWebアプリケーションがモバイルアプリなどを提供するためにAPIを採用するにつれて、この投稿の内容がより標準化されることを期待しています。間違いなく私の賛成票を持っています。素晴らしい答え
TheJKFever 2015

18

すべての答えの最良のものを組み合わせる:

...
if request.xhr?
  flash[:notice] = "Hello #{authorized_user.name}."
  flash.keep(:notice) # Keep flash notice around for the redirect.
  render :js => "window.location = #{jobs_path.to_json}"
else
...

あなたの答えをありがとう、私はそれを使いました。ただし、テストのために、このアクションをJSとしてリクエストしようとすると、CORS警告が発生します:ActionController :: InvalidCrossOriginRequest。これをテストに統合する方法について何か考えがありますか?
V.Déhaye

1
def redirect_to(options = {}, response_status = {})
  super(options, response_status)
  if request.xhr?
    # empty to prevent render duplication exception
    self.status = nil
    self.response_body = nil
    path = location
    self.location = nil

    render :js => "window.location = #{path.to_json}"
  end
end

0

コントローラのアクションを変更したくなかったので、このハックを思いつきました。

class ApplicationController < ActionController::Base
  def redirect_to options = {}, response_status = {}
    super

    if request.xhr?
      self.status        = 200
      self.response_body = "<html><body><script>window.location.replace('#{location}')</script></body></html>"
    end
  end
end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.