Rails 3: Как «redirect_to» в вызове Ajax?

72

Следующий метод 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 не работает.

Как бы вы решили это?

Теги:
redirect
ruby-on-rails-3

7 ответов

80
Лучший ответ

Наконец, я просто заменил

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

с этим:

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

и он отлично работает!

  • 11
    не жестко кодировать URL в контроллере ...
  • 10
    но вспышка не работает, не так ли?
Показать ещё 9 комментариев
47

Существует очень простой способ сохранить флэш для следующего запроса. В контроллере сделайте что-то вроде

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

flash.keep будет следить за тем, чтобы вспышка сохранялась для следующего запроса. Поэтому, когда отображается root_path, он отображает данное флэш-сообщение. Rails - потрясающий:)

  • 3
    Спасибо! понятия не имел о flash.keep
  • 0
    Что за Али!
25

Я думаю, что это немного лучше:

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

  • 12
    немного чуть лучше: render js: "window.location.pathname = #{jobs_path.to_json}"
22

В одном из моих приложений я использую 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? проверять. По мере того, как все больше веб-приложений применяют API для обслуживания мобильных приложений и тому подобное, я надеюсь, что в этом посте все станет более стандартизированным. Определенно есть мой upvote. Отличный ответ
15

Сочетание лучших ответов:

...
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
...
  • 0
    Хорошая подборка!
  • 0
    Спасибо за ваш ответ, я использовал это. Однако теперь для тестирования, когда я пытаюсь запросить это действие как JS, возникает предупреждение CORS: ActionController :: InvalidCrossOriginRequest. Есть ли у вас идеи, как интегрировать это в тестах?
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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню