Rails 3: How to "redirect_to" in Ajax call?

61,693

Solution 1

Finally, I just replaced

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

with this:

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

and it works fine!

Solution 2

There is very easy way to keep the flash for the next request. In your controller do something like

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

The flash.keep will make sure the flash is kept for the next request. So when the root_path is rendered, it will show the given flash message. Rails is awesome :)

Solution 3

I think this is slightly nicer:

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

Solution 4

In one of my apps, i use JSON to carry on the redirect and flash message data. It would look something like this:

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

And simple jQuery example would be:

$.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.
  }
})

Solution 5

Combining the best of all answers:

...
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
...
Share:
61,693
Misha Moroshko
Author by

Misha Moroshko

I build products that make humans happier. Previously Front End engineer at Facebook. Now, reimagining live experiences at https://muso.live

Updated on July 08, 2022

Comments

  • Misha Moroshko
    Misha Moroshko almost 2 years

    The following attempt_login method is called using Ajax after a login form is submitted.

    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
    

    The problem is that redirect_to doesn't work.

    How would you solve this ?

  • tokland
    tokland over 11 years
    slightly slightly nicer: render js: "window.location.pathname = #{jobs_path.to_json}"
  • mackmack
    mackmack over 11 years
    finally, this only renders the view and ignores the controller code that redirect_to would execute.
  • Mindey I.
    Mindey I. about 11 years
    This seems the best - would be nice to add a flash message in there also, but pretty nice.
  • Yarin
    Yarin over 10 years
    @mackmack - What do you mean? What controller code is getting ignored?
  • Jeff
    Jeff over 10 years
    wouldnt this require an eval call on the client?
  • zakelfassi
    zakelfassi over 10 years
    A better approach would be render :js => "window.location = '#{jobs_path}'"
  • justinxreese
    justinxreese almost 10 years
    It works, but wouldn't it be a lot better to pass back the redirect location with an actual json success message and do the redirecting on the front end?
  • smudge
    smudge over 9 years
    Isn't jobs_path basically as rigid as the URL? If the URL changes, so would the name of the route, unless you are being extra careful. Another alternative would be render js: "window.location = '#{polymorphic_path(@job.class)}'" and use the calculated resourceful route based on the Job model. This only works if your routes are resourceful and use standard naming conventions that line up with your models. (Or if you specify model_name on your models so that they generate the right route names.)
  • TheJKFever
    TheJKFever almost 9 years
    Lots of good information here. Good and proper use of the render :location, of the :status option, and the xhr? check. As more web applications adopt APIs to serve mobile apps and the such, I hope to see things in this post become more standardized. Definitely have my upvote. Great Answer
  • Tasos Anesiadis
    Tasos Anesiadis over 7 years
    Awesome. Anyone has any idea why the the simple redirect_to does not work?
  • V. Déhaye
    V. Déhaye over 7 years
    Thanks for your answer, I used it. However, now for the testing, when I try to request this action as JS, it raises a CORS warning : ActionController::InvalidCrossOriginRequest. Do you have any idea of how to integrate this in tests ?
  • MSC
    MSC over 5 years
    @Tasos Anesiadis, redirect_to doesn't work when the form is a 'remote' Rails form because the browser's been told to interpret the response from the controller as Javascript. You can see the redirect_to page in the Response tab (via the Network panel) of Chrome DevTools but what's needed instead is an instruction to the browser from the controller to go find a different page. The window.location solutions provided here, or changing the form to a regular 'local' form are required, unless you want to get into manually submitting and processing the form data via fetch() and JSON.