你如何使用Ajax请求处理Rail的flash?

时间:2008-12-14 08:40:04

标签: ruby-on-rails ajax

我很满意我提出的the solution。基本上,我有一个帮助方法重新加载闪存内联,然后我有一个after_filter,如果请求是xhr清除闪存。有人有比这更简单的解决方案吗?

更新:上述解决方案已在Rails 1.x中回写,不再受支持。

15 个答案:

答案 0 :(得分:62)

您还可以使用after_filter块将Flash消息存储在响应标头中,并使用javascript显示它们:

class ApplicationController < ActionController::Base
after_filter :flash_to_headers

def flash_to_headers
  return unless request.xhr?
  response.headers['X-Message'] = flash[:error]  unless flash[:error].blank?
  # repeat for other flash types...

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

在application.js中添加一个全局的ajax处理程序。对于jquery做这样的事情:

$(document).ajaxError(function(event, request) {
  var msg = request.getResponseHeader('X-Message');
  if (msg) alert(msg);
});

将alert()替换为您自己的javascript flash函数或尝试使用jGrowl。

答案 1 :(得分:29)

这是我的基于@emzero的版本,修改了jQuery,在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}"

答案 2 :(得分:15)

这在js响应中是必需的

如果您使用的是RSJ:

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

如果您使用的是jQuery:

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

答案 3 :(得分:14)

我是这样做的..

<强>控制器

respond_to do |format|
    flash.now[:notice] = @msg / 'blah blah...'
    format.html 
    format.js
  end

查看:

<div id='notice'>
    <%= render :partial => 'layouts/flash' , :locals => { :flash => flash } %>
</div>        

<强>布局/ _flash.html.erb

<% flash.each do |name, msg| %>
            <div class="alert-message info"> 
                <a class="close dismiss" href="#">x</a> 
                <p><%= msg %></p>
            </div>
<% end %>

<强> post.js.erb

$("#notice").html("<%= escape_javascript(render :partial => 'layouts/flash' , :locals => { :flash => flash }).html_safe %>");

答案 4 :(得分:9)

建立在其他人之上 -

(我们将完整的flash对象作为JSON传递,使我们能够在浏览器中重建完整的flash对象。这可以用来确保在Rails生成多个flash消息的情况下显示所有flash消息。)

#application_controller.rb
class ApplicationController < ActionController::Base
  after_filter :flash_to_headers

  def flash_to_headers
    if request.xhr?
      #avoiding XSS injections via flash
      flash_json = Hash[flash.map{|k,v| [k,ERB::Util.h(v)] }].to_json
      response.headers['X-Flash-Messages'] = flash_json
      flash.discard
    end
  end
end
//application.js
$(document).ajaxComplete(function(event, request){
  var flash = $.parseJSON(request.getResponseHeader('X-Flash-Messages'));
  if(!flash) return;
  if(flash.notice) { /* code to display the 'notice' flash */ $('.flash.notice').html(flash.notice); }
  if(flash.error) { /* code to display the 'error' flash */ alert(flash.error); }
  //so forth
}

答案 5 :(得分:6)

看起来你需要的是flash.now[:notice],它只在当前操作中可用,而在下一个操作中不可用。您可以在此处查看文档:{​​{3}}

答案 6 :(得分:5)

在控制器中分配消息,如下所示:

  flash.now[:notice] = 'Your message'

app / views / layouts / application.js.erb - Ajax请求的布局。 在这里你可以简单地使用

  <%= yield %>
  alert('<%= escape_javascript(flash.now[:notice]) %>'); 

或使用gritter的一些丰富动画:http://boedesign.com/demos/gritter/

  <%= yield %>
  <% if flash.now[:notice] %>
    $.gritter.add({
      title: '--',
      text: '<%= escape_javascript(flash.now[:notice]) %>'
    });
  <% end %>

答案 7 :(得分:3)

基于gudleik回答:

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

然后在你的application.js上(如果你使用的是Rails原生Prototype助手)添加:

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
   }
});

答案 8 :(得分:3)

我修改了Victor S的答案,以修复flash[type].blank?无法正常工作的一些案例,正如评论中很少有人所指出的那样。

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, nil].each do |type|
     return "" if type.nil?
     return flash[type] unless flash[type].blank?
   end
end

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

然后休息是一样的

// 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");
};

var show_ajax_message = function(msg, type) {
    $(".flash_message").html('<div class="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

});

答案 9 :(得分:3)

有一个名为Unobtrusive Flash的gem可以自动将Flash消息编码为cookie。客户端的javascript检查闪存并以您想要的任何方式显示它。这在普通和ajax请求中无缝地工作。

答案 10 :(得分:3)

这是我的版本(使用多重闪存通知和特殊字符UTF-8编码):

Inside ApplicationController:

after_filter :flash_to_headers
def flash_to_headers
  return unless request.xhr?
  [:error, :warning, :notice].each do |type|
    if flash[type]
      response.headers["X-Ajax-#{type.to_s.humanize}"] = flash[type]
    end
  end
  flash.discard
end

在我的咖啡脚本(twitter bootstrap版本)中:

css_class = {
    Notice: 'success',
    Warning: 'warning',
    Error: 'error'
}
$(document).ajaxComplete (event, request) ->
  for type in ["Notice", "Warning", "Error"]
    msg = request.getResponseHeader("X-Ajax-#{type}")
    if msg?
      $('#notices').append("<div class=\"alert #{css_class[type]}\">#{decodeURIComponent(escape(msg))}</div>")

答案 11 :(得分:1)

另一种方法是使用来自Ajax请求“OnFailure”处理程序的消息更新/显示“notice”div。它使您能够显示具有所需效果的这些Flash消息。我用过这个

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

在Javascript中

 new AjaxRequest(...

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

);

HTH

答案 12 :(得分:1)

我构建了一个引擎,其中包含一些application_controller的行为,以便像你们中的一些人提议的那样在响应头中发送flash消息。

https://github.com/bonzofenix/flajax

答案 13 :(得分:0)

我能想到的唯一改进是使page.reload_flash默认(不必将其放在每个rjs文件上,如果你不想重新加载flash,请将其设为expicit,如page.keep_flash。< / p>

我不知道从哪里开始,但知道一些导轨我确信它并不那么难。

答案 14 :(得分:0)

如果您想使用AJAX调用,则不应在控制器中使用redirect_to。相反,应明确表示flash消息:

在your_controller中:

respond_to :js

def your_ajax_method
  flash[:notice] = 'Your message!'
end

在your_ajax_method_in_the_controller命名的视图中

<强> your_ajax_method_in_the_controller.js.haml

:plain
  $("form[data-remote]")
    .on("ajax:success", function(e, data, status, xhr) {
      $('.messages').html("#{escape_javascript(render 'layouts/messages')}");
      setTimeout(function(){ $(".alert").alert('close') }, 5000);
    })

请注意,消息类是呈现消息的锚点。此类应存在于视图或应用程序布局中。如果您使用ERB,则该行变为$('.messages').html("<%= j(render 'layouts/messages') %>");

嵌入HAML / ERB的上述JavaScript是使用AJAX时显示flash消息的关键。对于非AJAX调用,所有其他组件保持不变。

您可以使用your_ajax_method_in_the_controller.js.coffee或普通.js,但JS / Coffee将无法使用rails变量。即使我不在这里使用变量,我更喜欢在HAML中包装JS以保持一致的代码库。

我利用Twitter Bootstrap来设置消息样式,因此$(".alert").alert('close')消除了通知。这是消息部分:

<强>布局/ _messages.html.haml

- flash.each do |name, msg|
  - if msg.is_a?(String)
    .alert-messages
      %div{class: "alert alert-#{name == :notice ? "success" : "error"} fade in"}
        %a.close{"data-dismiss" => "alert"} 
          %i.icon-remove-circle
        = content_tag :div, msg, id: "flash_#{name}"

以防万一,警报的CSS低于

.alert-messages {
  position: fixed;
  top: 37px;
  left: 30%;
  right: 30%;
  z-index: 7000;
}