ruby on rails - rails - ¿Cómo manejas el flash de Rail con las solicitudes de Ajax?
rails-ujs (15)
Aquí está mi versión (trabajando con avisos de flash múltiples y codificación de caracteres especiales UTF-8):
Dentro de 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
Dentro de mi coffee-script (versión de arranque de 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>")
Estoy muy contento con la solución que se me ocurrió. Básicamente, tengo un método de ayuda que recarga el flash en línea, y luego tengo un after_filter que borra el flash si la solicitud es xhr. ¿Alguien tiene una solución más simple que eso?
Actualización: la solución anterior fue escrita en Rails 1.x y ya no es compatible.
Asigna el mensaje en el controlador de esta manera:
flash.now[:notice] = ''Your message''
app / views / layouts / application.js.erb - Diseño para solicitudes Ajax. Aquí puedes simplemente usar
<%= yield %>
alert(''<%= escape_javascript(flash.now[:notice]) %>'');
o con algunas animaciones ricas usando gritter: http://boedesign.com/demos/gritter/
<%= yield %>
<% if flash.now[:notice] %>
$.gritter.add({
title: ''--'',
text: ''<%= escape_javascript(flash.now[:notice]) %>''
});
<% end %>
Basado en la respuesta 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
Luego, en su application.js (si usa Rails native Prototype helpers) agregue:
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
}
});
Construyendo sobre otros -
(Pasamos el objeto flash completo como JSON, lo que nos permite reconstruir el objeto flash completo en el navegador. Esto se puede utilizar para garantizar que todos los mensajes flash se muestren en caso de que Rails genere varios mensajes 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
}
Construyo un motor que incluye algún comportamiento para el controlador de la aplicación para enviar el mensaje flash en el encabezado de respuesta como algunos de ustedes proponen.
En caso de que quiera usar las llamadas AJAX, no se debe usar redirect_to en el controlador. Por el contrario, el mensaje flash debe indicarse explícitamente:
En tu_controlador:
respond_to :js
def your_ajax_method
flash[:notice] = ''Your message!''
end
En la vista que recibe el nombre de 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);
})
Tenga en cuenta que la clase de mensajes es un punto de anclaje para representar mensajes. Esta clase debe estar presente en su vista o diseño de la aplicación. Si usa ERB, la línea se convierte en $(''.messages'').html("<%= j(render ''layouts/messages'') %>");
El JavaScript anterior incrustado en HAML / ERB es la clave para mostrar mensajes flash cuando se usa AJAX. Todos los demás componentes siguen siendo los mismos para las llamadas que no son AJAX.
Puedes usar your_ajax_method_in_the_controller.js.coffee
o plain .js pero las variables de los rieles no estarán disponibles para JS / Coffee. Aunque no utilizo variables aquí, prefiero incluir JS en HAML para mantener una base de código consistente.
Aprovecho Twitter Bootstrap para diseñar mensajes, por lo tanto, $(".alert").alert(''close'')
desvanece el aviso. Y aquí están los mensajes parciales:
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}"
Por si acaso, CSS para las alertas está debajo
.alert-messages {
position: fixed;
top: 37px;
left: 30%;
right: 30%;
z-index: 7000;
}
Esto es necesario en la respuesta js
Si está utilizando RSJ:
page.replace_html :notice, flash[:notice]
flash.discard
Si está usando jQuery:
$("#flash_notice").html(<%=escape_javascript(flash.delete(:notice)) %>'');
Hay una gema llamada Desorden discreto que codifica automáticamente los mensajes flash en una cookie. Un javascript en el cliente comprueba flash y lo muestra de la forma que desee. Esto funciona a la perfección tanto en solicitudes normales como ajax.
La única mejora que puedo pensar es hacer que page.reload_flash sea el predeterminado (sin tener que ponerlo en cada archivo rjs, y hacer que expicite si no quieres volver a cargar el flash, algo así como page.keep_flash.
No sabría por dónde empezar, pero conociendo algunos rieles estoy seguro de que no es tan difícil.
Lo hice de esta manera ...
controlador :
respond_to do |format| flash.now[:notice] = @msg / ''blah blah...'' format.html format.js end
ver:
<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 %>");
Otra forma sería actualizar / mostrar el div "aviso" con el mensaje de su gestor de solicitudes Ajax "OnFailure". Le da la capacidad de mostrar estos mensajes flash con el efecto requerido. Usé esto
render :text => "Some error happened", :status => 444
en el Javascript
new AjaxRequest(... , OnFailure:function(transport) { $("#notice").update(transport.responseText); // show the message } );
HTH
Parece que lo que necesitas es flash.now[:notice]
, que solo está disponible en la acción actual y no en la siguiente. Puede consultar la documentación aquí: http://api.rubyonrails.com/classes/ActionController/Flash/FlashHash.html#M000327
También puede almacenar los mensajes flash en los encabezados de respuesta usando un bloque after_filter y mostrarlos usando 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
Y en application.js agrega un controlador global de ajax. Para jquery haz algo como esto:
$(document).ajaxError(function(event, request) {
var msg = request.getResponseHeader(''X-Message'');
if (msg) alert(msg);
});
Reemplace la alerta () con su propia función flash de JavaScript o pruebe jGrowl.
Y aquí está mi versión basada en @emzero, con modificaciones para trabajar con jQuery, probado en 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
});
diseño: application.html.haml
#flash-message
- flash.each do |name, msg|
= content_tag :div, msg, :id => "flash_#{name}"
flash[type].blank?
la respuesta de Victor S para arreglar algunos casos donde flash[type].blank?
no funcionó, como lo señalaron algunas personas en los comentarios.
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
Entonces el descanso es el mismo
// 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
});