您如何使用Ajax请求处理Rail的Flash?


77

我对提出的解决方案感到非常满意。基本上,我有一个帮助程序方法可以重新内联刷新Flash,然后有一个after_filter可以清除Flash(如果请求是xhr)。有没有人比这更简单的解决方案?

更新:上面的解决方案是用Rails 1.x写回的,不再受支持。


3
我喜欢您的解决方案。–after_filter { flash.discard if request.xhr? }
彼得·埃里希

Answers:


64

您还可以使用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);
});

用您自己的JavaScript Flash函数替换alert()或尝试使用jGrowl。


6
此外,您可以存储消息类型:(response.headers['X-Message-Type'] = flash_type而flash_type返回最重要的类型(错误>成功>通知)。此外,您还可以使用ajaxComplete它包括成功案例:$(document).ajaxComplete(function(e, request, opts) { fireFlash(request.getResponseHeader('X-Message'), request.getResponseHeader('X-Message-Type')); });
crispy 2010年

如果您的rails应用程序使用Prototype发出ajax请求,则先前的jquery处理程序将无法工作。您需要使用相应的Prototype处理程序。请参阅上面的答案。
emzero '02

1
为什么application.js顶部的注释表示不建议在其中添加代码?
史蒂夫

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

布局:application.html.haml

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

这也适用于导轨3.1.0。感谢Victor,为我开箱即用。
LearningRoR

2
伟大的编译,虽然不应该“ $(”#flash-message“)。ajaxComplete(function(event,request)”是“ $(document)”?
nullnullnull 2013年

由于某种原因,“除非使用flash [type] .blank?” 在某些情况下无法正常工作。不闪烁的动作将显示句子“错误,警告,注意”。我通过使用“ if(msg!=“错误,警告,注意”)show_ajax_message(msg,type)“这一行在js中对此进行了修补,但这显然是一个hacky解决方案。不过,我无法找出问题的真正原因。
nullnullnull

1
[:error, :warning, :notice].each {}如果不满足返回条件,则返回数组,因此此代码需要进行一些调整。
radixhound

3
如果重新编写代码以不返回字符串“错误,警告,通知”,并且不喜欢twitter-bootstrap gist.github.com/hbrandl/5253211
Hartwig

15

js响应中需要

如果您使用的是RSJ:

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

如果您使用的是jQuery:

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

3
看起来在Rails 3.1+中,您需要使用flash.discard(:notice)而不是flash.delete(:notice)
Adrian Macneil

15

我是这样做的。

控制器

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>        

layouts / _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 %>");

在您的控制器中,我可以看到一个错字,flash.now[:notice]= @msg / 'blah blah..'我也很好奇,您会将post.js.erb放在路径app / assets / javascripts中,对吗?
RajG 2013年

1
post.js.erb进入控制器的view文件夹,在本例中为“ views / posts / post.js.erb”。味精/“等等等等”,我的意思是味精或诸如“等等等等”之类的随机消息
dbKooper13年

10

建立在他人之上-

(我们将完整的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
}

谢谢 $.each( $.parseJSON(request.getResponseHeader('X-Flash-Messages')), function(key,value){ flash_message(key,value) });如果您具有js函数来显示具有Flash和Message的参数类型的Flash消息,则我将application.js中的函数修改为喜欢
Rahul garg

6

看起来您需要的是flash.now[:notice],仅在当前操作中可用,而在下一个操作中不可用。您可以在此处查看文档:http : //api.rubyonrails.com/classes/ActionController/Flash/FlashHash.html#M000327


嘿,我喜欢这个!没人吗?:S
Bachet 2012年

1
@le_Daf:使用flash.now是解决另一个问题的方法。flash.nowAjax回调不会神奇地将的内容插入当前页面。
混乱2012年

3
应该被称为flash。最终代替flash.now;)
Deborah

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

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

你能解释一下return unless request.xhr吗?我在当前请求结束的意思是如果我们已经添加任何闪光的通知,我们将它们添加到那么响应头在我们阅读这些JS -冷静,但我不知道是什么原因,我们有上述行
Michael K制作麦迪逊

3

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


为我解决了所有问题。
WM

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

});

3

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

在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引导程序版本)中:

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>")

它应该与utf8解码的亚洲字符一起工作;-)
Luc Boissaye 2014年

1
不,这不对!。我检查了RFC文档,发现http标头中仅支持ASCII字符。
fengd 2014年

1

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

 渲染:text =>“发生一些错误”,:status => 444

在Javascript中

 新的AjaxRequest(...

  ,
   OnFailure:function(transport){
      $(“#notice”)。update(transport.responseText);
     //显示讯息  
   }

);

高温超导



0

我能想到的唯一改进是将page.reload_flash设置为默认值(不必将其放置在每个rjs文件中,并且如果不想重新加载Flash,则可以使其显式显示,例如page.keep_flash。

我不知道从哪里开始,但知道一些技巧,我相信这并不难。


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或Plain .js,但是JS / Coffee将无法使用rails变量。即使我在这里不使用变量,我还是更喜欢将JS包装在HAML中以保持一致的代码库。

我利用Twitter Bootstrap对消息进行样式设置,从而$(".alert").alert('close')淡化了通知。这是部分消息

layouts / _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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.