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.
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.
Et voici ma version basée sur @emzero, avec des modifications pour fonctionner avec jQuery, testée sur Rails 3.2
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
// 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
});
#flash-message
- flash.each do |name, msg|
= content_tag :div, msg, :id => "flash_#{name}"
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)) %>');
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 %>");
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
}
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
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 %>
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
}
});
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
});
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.
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>")
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
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.
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.
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;
}