15

我正在编写一个 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中包含以下闪存显示逻辑...

...
%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 消息外,其他一切都按预期工作,我无法弄清楚原因

我怀疑这与我正在做的 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)
4

3 回答 3

23

我想权衡一下,因为需要将消息传递到标头似乎是一种循环方式,可以做一些 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 视图,最后,执行用 id="flash_messages" 替换我的 div 内容的 javascript。我的原始视图已通过 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 新手有帮助!

于 2014-08-04T15:51:50.033 回答
9

首先,感谢全面的问题

这是一个同样全面的答案:您如何处理带有 Ajax 请求的 Rail 的闪存?


阿贾克斯

我会认真看看你的Ajax刷新过程。刷新整个页面效率超低,我认为覆盖了更深层次的问题

不如试试用.js.erb文件views夹里的一个文件,直接处理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);
});
于 2014-01-10T10:52:14.577 回答
0

为协议的后遗症道歉,旨在作为对@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')
于 2021-01-15T23:38:27.267 回答