How do you handle Rail's flash with Ajax requests?

Viewed 49911

I'm pretty happy with the solution that I came up with. Basically, I have a helper method that reloads the flash inline, and then I have an after_filter that clear out the flash if the request is xhr. Does anyone have a simpler solution than that?

Update: The solution above was written back in Rails 1.x and is no longer supported.

15 Answers

And here is my version based on @emzero, with modifications to work with jQuery, tested on Rails 3.2

application_controller.rb

class ApplicationController < ActionController::Base
    protect_from_forgery

    after_filter :flash_to_headers

    def flash_to_headers
        return unless request.xhr?
        response.headers['X-Message'] = flash_message
        response.headers["X-Message-Type"] = flash_type.to_s

        flash.discard # don't want the flash to appear when you reload page
    end

    private

    def flash_message
        [:error, :warning, :notice].each do |type|
            return flash[type] unless flash[type].blank?
        end
    end

    def flash_type
        [:error, :warning, :notice].each do |type|
            return type unless flash[type].blank?
        end
    end
end

application.js

// FLASH NOTICE ANIMATION
var fade_flash = function() {
    $("#flash_notice").delay(5000).fadeOut("slow");
    $("#flash_alert").delay(5000).fadeOut("slow");
    $("#flash_error").delay(5000).fadeOut("slow");
};
fade_flash();

var show_ajax_message = function(msg, type) {
    $("#flash-message").html('<div id="flash_'+type+'">'+msg+'</div>');
    fade_flash();
};

$(document).ajaxComplete(function(event, request) {
    var msg = request.getResponseHeader('X-Message');
    var type = request.getResponseHeader('X-Message-Type');
    show_ajax_message(msg, type); //use whatever popup, notification or whatever plugin you want
});

layout: application.html.haml

        #flash-message
            - flash.each do |name, msg|
                = content_tag :div, msg, :id => "flash_#{name}"

This is needed in the js response

If you are using RSJ:

page.replace_html :notice, flash[:notice]
flash.discard

If you are using jQuery:

$("#flash_notice").html(<%=escape_javascript(flash.delete(:notice)) %>');

Based on gudleik answer:

class ApplicationController < ActionController::Base
  after_filter :flash_to_headers

def flash_to_headers
  return unless request.xhr?
  response.headers['X-Message'] = flash_message
  response.headers["X-Message-Type"] = flash_type

  flash.discard # don't want the flash to appear when you reload page
end

private

def flash_message
  [:error, :warning, :notice].each do |type|
    return flash[type] unless flash[type].blank?
  end
end

def flash_type
  [:error, :warning, :notice].each do |type|
    return type unless flash[type].blank?
  end
end

Then on your application.js (if you're using Rails native Prototype helpers) add:

Ajax.Responders.register({
onComplete: function(event, request) {
   var msg = request.getResponseHeader('X-Message');
   var type = request.getResponseHeader('X-Message-Type');
   showAjaxMessage(msg, type); //use whatever popup, notification or whatever plugin you want
   }
});

There is a gem called Unobtrusive Flash that automatically encodes flash messages into a cookie. A javascript at client end checks for flash and display it in whatever way you want. This works seamlessly in both normal and ajax requests.

Another way would be update/display the "notice" div with the message from the your Ajax requests "OnFailure" handler. It gives you the ability to show these flash messages with required effect. I used this

 render :text => "Some error happened", :status => 444

in the Javascript

 new AjaxRequest(...

  ,
   OnFailure:function(transport) {
      $("#notice").update(transport.responseText);
     // show the message  
   }

);

HTH

The only improvement I can think of is making the page.reload_flash default (not having to put it on every rjs file, and make it expicit if you don't want to reload the flash, something like page.keep_flash.

I wouldn't know where to start but knowing some rails I'm sure it's not that hard.

Related