ajax调用后Rails不显示flash消息
Posted
技术标签:
【中文标题】ajax调用后Rails不显示flash消息【英文标题】:Rails doesn't display flash messages after ajax call 【发布时间】:2014-01-28 17:26:19 【问题描述】:我正在编写一个 Rails 4.0.2 应用程序,并试图在 AJAX 事件后在我的视图中显示一个 Flash 通知。
在我看来,我会显示一个日历,其中包含用户可以点击的日期。当他们这样做时,我通过 onclick 事件处理程序触发 AJAX 事件,该事件处理程序更新我的模型,添加或删除记录。触发事件后,我完成了页面刷新以显示更新的结果。
我发现我必须在 JS 点击事件期间进行页面刷新,才能正确更新视图。仅在控制器中进行重定向或渲染是不够的。
因此,为此,我在控制器中设置了 Flash 通知...
def set_conflicts
@conflict = @member.conflicts.for_date(params[:date]).first
if @conflict
@conflict.destroy
else
conflict_date = Date.parse(params[:date])
unless Conflict.create(
month: conflict_date.month,
day: conflict_date.day,
year: conflict_date.year,
member: @member
)
flash[:alert] = 'Oops, there was a problem updating conflicts'
end
end
flash[:notice] = 'This is a test!'
redirect_to manage_member_conflicts_path(@member)
end
...并在我的 application.haml...
中包含以下 Flash 显示逻辑...
%body
= p flash.inspect
- flash.each do |type, message|
= content_tag(:div, message, :class => "flash-#type")
= render 'devise/menu/login_items'
= yield
注意:我在视图中使用 HAML 而不是 ERB
然而,无论我尝试什么,Flash 消息都不会显示。其他一切都按预期工作除了 flash message,我无法弄清楚为什么。
我怀疑这与我正在做的 AJAX 刷新与重定向(甚至可能是 turbolinks)有关,但我已经在 SO 上查看了其他答案,但根本无法让它工作。显然,我错过了一些东西。
这是 JS 点击处理程序(非常简单):
window.calendarClick = (eventDate, linkURL) ->
event = $(document.getElementById(eventDate))
event.toggleClass('selected')
# Set or Remove conflicts
$.ajax
url: linkURL
data:
date: eventDate
# Refresh the page
$.ajax
url: '',
context: document.body,
success: (s,x) ->
$(this).html(s)
【问题讨论】:
【参考方案1】:我想权衡一下,因为需要将消息传递到标头似乎是一种循环方式,可以做一些 Rails 应该已经具备的能力。
经过几个小时的搜索,我意识到我已经通过在控制器中传递以下行来做需要做的事情:
flash[:error] = "My flash message error."
respond_to do |format|
format.js
end
flash[:error] 为以下视图设置 Flash 消息,respond_to 意识到我在我的 link_to 中设置了 remote: true (原始 ajax 请求):
<%= link_to "My Ajax Request", some_path(@some_value), remote: true %>
并以 javascript 响应链接。如果您仍然希望能够在没有将远程设置为 true 的情况下调用同一个控制器,则可以在此块中包含 format.html。
在我的 application.html.erb 中,我有一个呈现 flash 消息的部分:
<%= render "layouts/flash_notices" %>
该部分包含一个如下所示的 div:
<div id="flash_messages">
<% flash.each do |type, message| %>
<p><%= type %>: <%= message %></p>
<% end %>
</div>
最后我创建了一个名为 MY_CONTROLLER_ACTION_NAME.js.erb 的视图并插入:
<% flash.each do |type, message| %>
$("#flash_messages").html("<%= type.to_s.humanize %>: <%= message.html_safe %>")
<% end %>
现在,当我单击启用 AJAX 的链接时,将执行控制器操作,设置 flash 消息,控制器加载 js.erb 视图,最后,javascript 将我的 div 的内容替换为 id="flash_messages"被执行。我的原始视图已通过 Flash 消息更新,世界一切正常。
注意:如果您使用的是 Bootstrap 或其他一些特殊的 CSS 框架,您可以在 js.erb 文件的 $("#flash_messages').html(" "); 调用中包含该代码。我将我的 Bootstrap 代码包含为如下:
$("#flash_messages').html("<div class='alert <%= bootstrap_class_for(type) %> alert-dismissible' role='alert'><button class='close' data-dismiss='alert'>×</button><%= message.html_safe %></div>")
我知道这个问题是几个月前发布的,但是在搜索这个确切的问题时,响应似乎总是你需要通过标题发送 Flash 消息。我想证明还有另一种方法。希望这对我未来的 Rails 新手有帮助!
【讨论】:
我喜欢它,这基本上就是我最终做的。谢谢 Celsian! 我写这个答案已经有几年了,但对于那些想知道的人来说,它仍然适用于 Rails 5。 谢谢,这正是我想要的!【参考方案2】:首先,感谢您提出全面的问题
这是一个同样全面的答案:How do you handle Rail's flash with Ajax requests?
Ajax
我会认真查看您的Ajax
刷新过程。刷新整个页面效率超低,我觉得涉及到更深层次的问题
不如试试用views
文件夹下的.js.erb
文件,直接用方法处理JS:
def set_conflicts
@conflict = @member.conflicts.for_date(params[:date]).first
if @conflict
@conflict.destroy
else
conflict_date = Date.parse(params[:date])
unless Conflict.create(
month: conflict_date.month,
day: conflict_date.day,
year: conflict_date.year,
member: @member
)
flash[:alert] = 'Oops, there was a problem updating conflicts'
end
end
flash[:notice] = 'This is a test!'
respond_to do |format|
format.html redirect_to manage_member_conflicts_path(@member)
format.js
end
end
#app/views/controller/set_conflicts.js.erb
$("#your_element").html(<%=j render manage_member_conflicts_path(@member) ) %>);
回应
虽然我不确定这一点,但我知道 Flash 对象由ActionDispatch::Flash 中间件处理。我相信 XHR 的处理方式与标准 HTTP 请求不同,因此会阻止 Flash 在您的响应中显示
这意味着您必须做一些事情来通过您的 Ajax 请求传递 Flash 对象。根据我在此答案顶部发布的问题,您可以使用response headers
来做到这一点:
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
$(document).ajaxError(function(event, request)
var msg = request.getResponseHeader('X-Message');
if (msg) alert(msg);
);
【讨论】:
很好的答案,谢谢 Rich。我的主要问题是找到一种有效的方法来根据用户点击的日期添加/删除记录。我将按照您的建议调查重构。我已经尝试了几种将请求标头设置为无效的方法,但我确定这只是我对 JS 再次显示的缺乏经验。 想聊聊吗?这是你应该能够相对简单地做到的事情 我会的,如果/当你有时间的时候。我在聊天中创建了一个房间...chat.***.com/rooms/44968/chat-with-donovan 我决定使用从railscasts.com/episodes/136-jquery-ajax-revised 收集的信息进行重构。 A)它不会依赖于 Java 的启用和 B)我相信这是一种更清洁的方法。所以,我将把它标记为已接受的答案,因为你让我走上了启蒙之路,但我仍然会对你在聊天中的看法感兴趣。谢谢! 如果你愿意,我现在可以聊天!【参考方案3】:为违反协议表示歉意,旨在作为对@Celsian 回答的评论,但我发现我缺乏声誉点...
@Celsian,谢谢你,这很有帮助。
但是,使用flash[:error]
意味着闪存将在页面刷新后徘徊。
flash.now[:error] = "My flash message error."
解决了这个问题。
还涵盖了非 AJAX 情况,我选择了:
respond_to do |format|
format.html flash[:error] = "error"
redirect_to request.referrer
format.js flash.now[:error] = "error"
end
这样,无论请求是不是 AJAX,flash 都会到达(并且不会超过它的受欢迎程度)。
我还在您的引导增强代码行中发现了一个小错字:
$("#flash_messages')
应该是
$('#flash_messages')
【讨论】:
以上是关于ajax调用后Rails不显示flash消息的主要内容,如果未能解决你的问题,请参考以下文章