Comment gérer flash Rail avec les demandes Ajax?

je suis plutôt content de la solution que j'ai trouvé. Fondamentalement, j'ai une méthode helper qui recharge le flash inline, et puis j'ai un after_filter qui nettoie le flash si la requête est xhr. Quelqu'un aurait-il une solution plus simple que cela?

mise à Jour: La solution ci-dessus a été écrit dans les Rails 1.x et n'est plus pris en charge.

74
demandé sur Stephen Horvath 2008-12-14 11:40:04

15 réponses

vous pouvez également stocker les messages flash dans les en-têtes de réponse en utilisant un bloc after_filter et les afficher en utilisant 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

et en application.js ajoute un gestionnaire ajax global. Pour jquery faire quelque chose comme ceci:

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

remplacez alert () par votre propre fonction flash javascript ou essayez jGrowl.

61
répondu gudleik 2013-03-28 10:54:39

et voici ma version basée sur @emzero, avec des modifications à travailler avec jQuery, testé sur 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

de l'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
});

mise en page: demande.HTML.haml

        #flash-message
            - flash.each do |name, msg|
                = content_tag :div, msg, :id => "flash_#{name}"
29
répondu Victor S 2015-07-01 22:18:08

C'est nécessaire dans le js de réponse

si vous utilisez RSJ:

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

si vous utilisez jQuery:

$("#flash_notice").html(<%=escape_javascript(flash.delete(:notice)) %>');
15
répondu Silviu Postavaru 2009-01-08 11:28:14

Je l'ai fait comme ça..

contrôleur :

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

après.js.erb

$("#notice").html("<%= escape_javascript(render :partial => 'layouts/flash' , :locals => { :flash => flash }).html_safe %>");
14
répondu dbKooper 2012-01-15 21:40:48

Construire sur le dessus des autres -

(nous passons l'objet Flash complet comme JSON, nous permettant de reconstruire l'objet flash complet dans le navigateur. Ceci peut être utilisé pour s'assurer que tous les messages flash sont affichés au cas où plusieurs messages flash sont générés par des Rails.)

#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
}
9
répondu Vikrant Chaudhary 2013-03-15 06:25:59

ressemble à ce que vous avez besoin est flash.now[:notice] , qui est seulement disponible dans l'action actuelle et non dans la prochaine. Vous pouvez consulter la documentation ici: http://api.rubyonrails.com/classes/ActionController/Flash/FlashHash.html#M000327

6
répondu nakajima 2009-01-01 18:51:45

Attribuer le message dans le contrôleur comme ceci:

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

app/views/layouts/de l'application.js.erb-Layout pour les requêtes Ajax. Ici, vous pouvez simplement utiliser

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

ou avec quelques animations riches en gritter: http://boedesign.com/demos/gritter /

  <%= yield %>
  <% if flash.now[:notice] %>
    $.gritter.add({
      title: '--',
      text: '<%= escape_javascript(flash.now[:notice]) %>'
    });
  <% end %>
5
répondu Arun Kumar Arjunan 2011-08-05 11:08:39

basé sur la réponse de 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

puis sur votre demande.js (si vous utilisez des Rails natif aides Prototype) d'ajouter:

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
   }
});
3
répondu emzero 2011-02-15 18:09:28

il y a un bijou appelé Flash discret qui Code automatiquement les messages flash dans un cookie. Un javascript à la fin du client vérifie pour flash et l'afficher de n'importe quelle façon que vous voulez. Cela fonctionne parfaitement dans les requêtes normales et ajax.

3
répondu lulalala 2012-03-26 01:49:40

j'ai modifié la réponse de Victor S pour corriger certains cas où flash[type].blank? n'a pas fonctionné comme noté par peu de gens dans les commentaires.

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

puis le repos est le même

// 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
répondu Ricky Gu 2013-09-07 23:37:58

voici ma version (travaillant avec des notices flash multiples et des caractères spéciaux encodage UTF-8):

Internal 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

à l'Intérieur de mon café-script (twitter bootstrap version):

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>")
3
répondu Luc Boissaye 2014-02-26 16:49:20

une autre façon serait de mettre à jour/Afficher le div" notice "avec le message du gestionnaire" OnFailure " de vos requêtes Ajax. Il vous donne la capacité de montrer ces messages flash avec l'effet requis. J'ai utilisé ce

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

dans le Javascript

 new AjaxRequest(...

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

);

HTH

1
répondu Rishav Rastogi 2009-03-11 12:38:22

je construis un moteur qui inclut un certain comportement à l'application_controller pour envoyer le message flash dans l'en-tête de réponse comme certains d'entre vous le proposent.

https://github.com/bonzofenix/flajax

1
répondu bonzofenix 2013-01-15 15:06:00

la seule amélioration à laquelle je peux penser est de faire la page.reload_flash par défaut (ne pas avoir à le mettre sur chaque fichier rjs, et le faire expicit si vous ne voulez pas recharger le flash, quelque chose comme la page.keep_flash.

Je ne saurais pas par où commencer mais connaissant quelques rails je suis sûr que ce n'est pas si difficile.

0
répondu krusty.ar 2008-12-15 11:24:36

dans le cas où vous voulez utiliser AJAX appels redirect_to ne doit pas être utilisé dans le contrôleur. Au lieu de cela, le message flash devrait être explicitement dénoté:

Dans your_controller:

respond_to :js

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

dans la vue qui est nommée par 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);
    })

s'il vous Plaît, notez que l' messages la classe est un point d'ancrage pour le rendu des messages. Cette classe doit être présente dans votre vue ou la mise en page de l'application. Si vous utilisez ERB la ligne devient $('.messages').html("<%= j(render 'layouts/messages') %>");

le JavaScript ci-dessus incorporé dans HAML/ERB est la clé pour afficher des messages flash lors de L'utilisation D'AJAX. Tous les autres composants restent les mêmes pour les appels non-AJAX.

vous pouvez utiliser your_ajax_method_in_the_controller.js.coffee ou simple .js mais alors les variables de rails ne seront pas disponibles pour Js / Coffee. Même si je n'utilise pas de variables ici, je préfère envelopper JS dans HAML pour garder une base de données cohérente.

j'utilise Twitter Bootstrap pour les messages de style, donc $(".alert").alert('close') disparaît l'avis. Et voici les messages partial:

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

juste au cas où, CSS pour les alertes est inférieur à

.alert-messages {
  position: fixed;
  top: 37px;
  left: 30%;
  right: 30%;
  z-index: 7000;
}
0
répondu Vadym Tyemirov 2013-07-02 21:52:16