skwidbreth
skwidbreth

Reputation: 8424

Rails respond_to redirect not working

The respond_to block in a create controller in my Rails app is not redirecting on a successful save... I'm sure this is a simple solution, but I am inexperienced with Rails and this is the first time that I am encountering this problem.

The form is set so that :remote => true, and the controller is as follows...

def create
    @store = current_user.stores.new(store_params)

    respond_to do |format|
        if @store.save
            format.html { redirect_to root_path }
        else
            format.html { flash[:alert] = "Save failed! #{@store.errors.full_messages.join(";")}" 
            render "new" }
            format.js {}
        end
    end
end

And while I'm on the subject, the code from the else portion of the conditional doesn't run either, except for format.js {}, which does run the code in my create.js.erb file (an alert, for the time being).

I'm working with Rails 4.2.5. Can someone help me to understand why the redirect and the alert are not working? Thank you!

EDITING TO SHOW SOLUTION Based on Rich's answer, here's the solution that I came up with:

Controller:

def create
    @store = current_user.stores.new(store_params)

    flash.now[:alert] = "Save failed! #{@store.errors.full_messages.join(";")}" unless @store.save

    respond_to do |format|
        format.js
    end

    if @store.save
        flash[:notice] = "New store created"
    end
end

create.js.erb

<% if flash.now[:alert] %>
    $("#alert_holder").empty();
    $("#alert_holder").append("<%= j flash.now[:alert] %>");
<% else %>
    window.location.href = "<%= root_url %>";
<% end %>

Note that I needed to add quotes around the redirect url.

On form success, the page redirects to root. On failure, the error message flashes but the form is not refreshed - any answers the user has entered remain.

Upvotes: 3

Views: 5608

Answers (2)

Richard Peck
Richard Peck

Reputation: 76774

remote: true is an ajax request.

Ajax is javascript, and as such will invoke the format.js method:

def create
    @store = current_user.stores.new store_params

    respond_to do |format|
        if @store.save
            format.js
            format.html { redirect_to root_path }
        else
            format.js
            format.html { flash[:alert] = "Save failed! #{@store.errors.full_messages.join(";")}" 
            render "new" }
        end
    end
end

The format.js method will call the /app/views/[:controller]/[:action].js.erb file, which will fire any of the JS you have inside it.

If you don't want to have the js format handling the response, you'll have to do away with respond_to and just have what you'd like to return (redirect_to won't work).


Ajax

There are several stipulations you need to appreciate with this:

  • Ajax cannot "redirect" (on its own)
  • Ajax will be treated as JS in your Rails controller
  • You have to "hack" the flash to get it working through JS

If you don't have experience with Ajax, the simple explanation is that it's a "pseudo-request"; it sends an HTTP request without having to reload the browser.

The pattern for Ajax is simple: Ajax request > server > Ajax response

You cannot "redirect" via Ajax unless you parse the response with javascript. As the Ajax acronym (Asynchronous Javascript And XML) suggests, the response is expected to be XML (IE no functionality).

--

To answer your question, you'll need to use flash.now for the "flash" message, and handle the response with your .js.erb file:

def create
    @store = current_user.stores.new store_params
    flash.now[:alert] = "Save failed! #{@store.errors.full_messages.join(";")}" unless @store.save

    respond_to do |format|
        format.js
        format.html
    end
end

This will allow you to call...

#app/views/stores/create.js.erb
<% if flash.now[:alert] %> alert("<%=j flash.now[:alert] %>"); <% end %>
window.location.href = <%= root_url %>;

Ref


Your new code can be improved a little :

def create
    @store = current_user.stores.new store_params

    if @store.save
        flash[:notice] = "New store created"
    else
        flash.now[:alert] = "Save failed! #{@store.errors.full_messages.join(";")}"
    end

    respond_to do |format|
        format.js
    end
end

If you wanted to DRY up your code even more, you'll want to look at the responders gem:

#app/controllers/stores_controller.rb
class StoresController < ApplicationController
   respond_to :js, only: :create

   def create
      @store = ...
      respond_with @store if @store.save
   end
end

Upvotes: 6

Ho Man
Ho Man

Reputation: 2345

If you have remote: true in your form, the format that is detected by the controller will be format.js, which is not present in your successful @store.save section.

2 options:

  • Default to normal form submit (by removing remote: true)
  • Load another js.erb file by adding format.js just like in the else clause then do the error handling there via some javascript.

Upvotes: 2

Related Questions