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.
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.
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}"
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)) %>');
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 %>");
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
}
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
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 %>
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
}
});
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.
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
});
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>")
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
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.
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.
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;
}