Rails 3: ¿Cómo "redirect_to" en una llamada Ajax?


85

El siguiente attempt_loginmétodo se llama usando Ajax después de que se envía un formulario de inicio de sesión.

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

El problema es que eso redirect_tono funciona.

Como resolverias esto ?

Respuestas:


102

Finalmente, acabo de reemplazar

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

con este:

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

¡y funciona bien!


43
Un mejor enfoque seríarender :js => "window.location = '#{jobs_path}'"
zakelfassi

3
Funciona, pero ¿no sería mucho mejor devolver la ubicación de redireccionamiento con un mensaje de éxito de json real y hacer el redireccionamiento en la interfaz?
justinxreese

1
¿No es jobs_pathbásicamente tan rígido como la URL? Si la URL cambia, también lo hará el nombre de la ruta, a menos que tenga mucho cuidado. Otra alternativa sería render js: "window.location = '#{polymorphic_path(@job.class)}'"utilizar la ruta ingeniosa calculada basada en el modelo de trabajo. Esto solo funciona si sus rutas son ingeniosas y usan convenciones de nomenclatura estándar que se alinean con sus modelos. (O si especifica nombre_del_modelo en sus modelos de modo que generen los nombres de las rutas correctas.)
mancha

2
Increíble. ¿Alguien tiene alguna idea de por qué el simple redirect_to no funciona?
Tasos Anesiadis

1
@Tasos Anesiadis, redirect_to no funciona cuando el formulario es un formulario de Rails 'remoto' porque se le ha dicho al navegador que interprete la respuesta del controlador como Javascript. Puede ver la página redirect_to en la pestaña Respuesta (a través del panel Red) de Chrome DevTools, pero lo que se necesita en su lugar es una instrucción para el navegador desde el controlador para buscar una página diferente. Se requieren las soluciones de window.location que se proporcionan aquí, o cambiar el formulario a un formulario 'local' regular, a menos que desee enviar y procesar manualmente los datos del formulario a través de fetch () y JSON.
MSC

67

Existe una forma muy sencilla de mantener el flash para la próxima solicitud. En tu controlador haz algo como

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

Se flash.keepasegurará de que el flash se mantenga para la próxima solicitud. Entonces, cuando root_pathse procese, mostrará el mensaje flash dado. Rails es increíble :)


28

Creo que esto es un poco mejor:

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


12
un poco más agradable:render js: "window.location.pathname = #{jobs_path.to_json}"
tokland

26

En una de mis aplicaciones, uso JSON para llevar a cabo la redirección y los datos de los mensajes flash. Se vería así:

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

Y un ejemplo simple de jQuery sería:

$.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
Mucha buena información aquí. Buen y correcto uso del render: location, de la opción: status y del xhr? cheque. A medida que más aplicaciones web adopten API para servir aplicaciones móviles y demás, espero que las cosas en esta publicación se vuelvan más estandarizadas. Definitivamente tengo mi voto a favor. Gran respuesta
TheJKFever

18

Combinando la mejor de todas las respuestas:

...
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
...

Gracias por tu respuesta, lo usé. Sin embargo, ahora para la prueba, cuando intento solicitar esta acción como JS, aparece una advertencia de CORS: ActionController :: InvalidCrossOriginRequest. ¿Tiene alguna idea de cómo integrar esto en las pruebas?
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

No quería modificar las acciones de mi controlador, así que se me ocurrió este truco:

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.