web-dev-qa-db-fra.com

Comment gérez-vous le flash de Rail avec les requêtes Ajax?

Je suis assez content de la solution que j'ai trouvé. Fondamentalement, j'ai une méthode d'assistance qui recharge le flash en ligne, puis j'ai un after_filter qui efface le flash si la demande est xhr. Quelqu'un at-il une solution plus simple que cela?

Mise à jour: La solution ci-dessus a été réécrite en Rails 1.x et n'est plus prise en charge.

75
Jerry Cheung

Vous pouvez également stocker les messages flash dans les en-têtes de réponse à l'aide d'un bloc after_filter et les afficher à l'aide de 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 dans application.js, ajoutez un gestionnaire ajax global. Pour jquery, faites 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.

62
gudleik

Et voici ma version basée sur @emzero, avec des modifications pour fonctionner avec jQuery, testée 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

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: application.html.haml

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

Ceci est nécessaire dans la réponse js

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
Silviu Postavaru

Je l'ai fait de cette façon ..

contrôleur:

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

voir:

<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 %>");
14
dbKooper

Miser sur les autres -

(Nous transmettons l'objet flash complet au format JSON, ce qui nous permet de reconstruire l'objet flash complet dans le navigateur. Cela peut être utilisé pour garantir que tous les messages flash sont affichés au cas où plusieurs messages flash seraient générés par 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
}
10
Vikrant Chaudhary

On dirait que ce dont vous avez besoin est flash.now[:notice], qui n'est disponible que dans l'action en cours et non dans la suivante. Vous pouvez consulter la documentation ici: http://api.rubyonrails.com/classes/ActionController/Flash/FlashHash.html#M000327

6
nakajima

Attribuez le message dans le contrôleur comme ceci:

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

app/views/layouts/application.js.erb - Disposition pour les demandes Ajax. Ici, vous pouvez simplement utiliser

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

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

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

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

Ensuite, sur votre application.js (si vous utilisez Rails helpers natifs du prototype), ajoutez:

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
emzero

J'ai modifié la réponse de Victor S pour corriger certains cas où flash[type].blank? n'a pas fonctionné comme l'ont noté quelques personnes 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

Alors 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
Ricky Gu

Il existe un joyau appelé Flash discret qui encode automatiquement les messages flash dans un cookie. Un javascript à la fin du client vérifie le flash et l'affiche comme vous le souhaitez. Cela fonctionne parfaitement dans les requêtes normales et ajax.

3
lulalala

Voici ma version (fonctionnant avec plusieurs notifications flash et encodage UTF-8 de caractères spéciaux):

À l'intérieur d'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
Luc Boissaye

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 possibilité d'afficher ces messages flash avec l'effet requis. J'ai utilisé ça

 render: text => "Une erreur s'est produite",: status => 444 

dans le Javascript

 nouveau AjaxRequest (... 
 
, 
 OnFailure: fonction (transport) {
 $ ("# notice"). update (transport .responseText); 
 // affiche le message 
} 
 
); 
 

HTH

1
Rishav Rastogi

Je construis un moteur qui inclut un 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
bonzofenix

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

Je ne saurais pas par où commencer mais en connaissant certains Rails Je suis sûr que ce n'est pas si difficile.

0
krusty.ar

Si vous souhaitez utiliser AJAX redirect_to ne doivent pas être utilisés dans le contrôleur. Au lieu de cela, le message flash doit être explicitement indiqué:

Dans votre_contrôleur:

respond_to :js

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

Dans la vue nommée par votre_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);
    })

Veuillez noter que la classe messages est un point d'ancrage pour le rendu des messages. Cette classe doit être présente dans la vue ou la disposition de votre application. Si vous utilisez ERB, la ligne devient $('.messages').html("<%= j(render 'layouts/messages') %>");

Le JavaScript ci-dessus intégré à 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 .js ordinaire mais les variables Rails ne seront pas disponibles pour JS/Coffee. Même si je n'utilise pas de variables ici, je préfère encapsuler JS en HAML pour conserver une base de code cohérente.

Je tire parti de Twitter Bootstrap pour styliser les messages, donc $(".alert").alert('close') fane loin l'avis. Et voici les messages partiel:

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 ci-dessous

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