简体   繁体   English

Rails 在 ajax 调用后不显示 Flash 消息

[英]Rails doesn't display flash messages after ajax call

I'm writing a rails 4.0.2 app, and am trying to get a Flash notice to display in my view after an AJAX event.我正在编写一个 rails 4.0.2 应用程序,并试图在 AJAX 事件之后让 Flash 通知显示在我的视图中。

In my view I display a calendar with days a user can click on.在我看来,我显示了一个日历,其中包含用户可以点击的天数。 When they do so, I fire an AJAX event via an onclick event handler which updates my model, either adding or removing a record.当他们这样做时,我通过 onclick 事件处理程序触发一个 AJAX 事件,该事件处理程序更新我的模型,添加或删除记录。 After firing the event, I complete a page refresh in order to display the updated results.触发事件后,我完成页面刷新以显示更新的结果。

I found I had to do a page refresh during the JS click event in order to get the view to update properly.我发现我必须在 JS 单击事件期间刷新页面才能正确更新视图。 A redirect or render in the controller alone wasn't enough.仅在控制器中进行重定向或渲染是不够的。

So, to that effect, I have set a Flash notice in my controller...因此,为此,我在控制器中设置了 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

... and have included the following flash display logic in my application.haml ... ...并在我的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

Note: I use HAML instead of ERB in my views注意:我在视图中使用 HAML 而不是 ERB

Yet, no matter what I try, the Flash message does not display.然而,无论我尝试什么,Flash 消息都不会显示。 Everything else works as expected except the flash message , and I haven't been able to figure out why .除了flash 消息,其他一切都按预期工作,我一直无法弄清楚原因

I suspect it's got something to do with the AJAX refresh I'm doing combined with the redirect (and maybe even turbolinks) but I've looked through other answers here on SO and simply can't get it to work.我怀疑这与我正在结合重定向(甚至可能是 turbolinks)进行的 AJAX 刷新有关,但我已经在 SO 上查看了其他答案,但根本无法使其正常工作。 Clearly, I'm missing something.显然,我错过了一些东西。

Here's the JS click handler (it's pretty simple):这是 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)

I'd like to weigh in as the need to pass messages to the header seemed like a round-a-bout way of doing something Rails should already be capable of.我想权衡一下,因为需要将消息传递给标头似乎是做一些 Rails 应该已经能够做到的事情的周而复始的方式。

After several hours of searching I realized I was already doing what needed to be done by passing the following lines in my controller:经过几个小时的搜索,我意识到我已经在通过在我的控制器中传递以下几行来做需要做的事情:

flash[:error] = "My flash message error."

respond_to do |format|
  format.js 
end

The flash[:error] sets the Flash message for the following view, the respond_to realizes that I have remote: true set in my link_to (original ajax request): flash[:error] 为以下视图设置 Flash 消息,respond_to 意识到我在我的 link_to 中设置了 remote: true (原始 ajax 请求):

<%= link_to "My Ajax Request", some_path(@some_value), remote: true %>

and responds to the link as JavaScript.并作为 JavaScript 响应链接。 You can include format.html in this block if you still want to have the ability to call that same controller without remote set to true.如果您仍然希望能够在不将 remote 设置为 true 的情况下调用相同的控制器,则可以在此块中包含 format.html。

In my application.html.erb I have a partial that renders flash messages:在我的 application.html.erb 中,我有一个部分呈现 Flash 消息:

<%= render "layouts/flash_notices" %>

and that partial contains a div that looks like the following:并且该部分包含一个如下所示的 div:

<div id="flash_messages">
  <% flash.each do |type, message| %>
    <p><%= type %>: <%= message %></p>
  <% end %>
</div>

Finally I created a view called MY_CONTROLLER_ACTION_NAME.js.erb and inserted:最后我创建了一个名为 MY_CONTROLLER_ACTION_NAME.js.erb 的视图并插入:

<% flash.each do |type, message| %>
  $("#flash_messages").html("<%= type.to_s.humanize %>: <%= message.html_safe %>")
<% end %>

Now when I click my AJAX enabled link, the controller action is executed, the flash message is set, the controller loads the js.erb view and finally, the javascript to replace the contents of my div with id="flash_messages" is executed.现在,当我单击启用 AJAX 的链接时,将执行控制器操作,设置 flash 消息,控制器加载 js.erb 视图,最后执行用 id="flash_messages" 替换我的 div 内容的 javascript。 My original view is updated with the flash message and all is right with the world.我的原始视图已使用 Flash 消息更新,世界一切正常。

Note: If you're using Bootstrap or some other special CSS framework you can include that code in the js.erb file's $("#flash_messages').html(" "); call. I included my Bootstrap code as follows:注意:如果您使用 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>")

I know this question was posted several months ago, but when searching for this exact issue the response always seems to be that you need to send the flash messages via the headers.我知道这个问题是几个月前发布的,但是在搜索这个确切的问题时,响应似乎总是您需要通过标题发送 Flash 消息。 I wanted to show there is another method.我想展示另一种方法。 Hopefully this will help my fellow Rails newbies of the future!希望这对我未来的 Rails 新手有所帮助!

Firstly, thanks for comprehensive question首先,感谢全面的问题

Here is an equally comprehensive answer: How do you handle Rail's flash with Ajax requests?这是一个同样全面的答案: 您如何处理带有 Ajax 请求的 Rail 闪存?


Ajax阿贾克斯

I'd seriously look at your Ajax refresh process.我会认真查看您的Ajax刷新过程。 Refreshing the whole page is super inefficient, and I think covering a deeper issue刷新整个页面效率非常低,我认为涵盖了更深层次的问题

Why don't you try using a .js.erb file in the views folder, to handle JS directly by the method:你为什么不尝试使用.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) ) %>);

Response回复

Although I don't know this for sure, I know the Flash objects are handled by ActionDispatch::Flash middleware.虽然我不确定这一点,但我知道 Flash 对象是由ActionDispatch::Flash中间件处理的。 I believe this is not handled for XHR in the same way as standard HTTP requests, consequently preventing Flash from being shown in your response我相信 XHR 的处理方式与标准 HTTP 请求的处理方式不同,因此会阻止 Flash 显示在您的响应中

This means you'll have to do something to pass the Flash object through your Ajax request.这意味着您必须做一些事情才能通过 Ajax 请求传递 Flash 对象。 And according to the question I posted at the top of this answer, you can use the response headers to do it:根据我在此答案顶部发布的问题,您可以使用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);
});

Apologies for the breech of protocol, intended as a comment to @Celsian's answer but I found I lack reputation points for that...为协议的后遗症道歉,旨在作为对@Celsian 回答的评论,但我发现我缺乏声誉点......

@Celsian, thank you this was very helpful. @Celsian,谢谢,这很有帮助。 Using flash[:error] however means the flash will linger after a page refresh.然而,使用flash[:error]意味着 flash 将在页面刷新后逗留。 flash.now[:error] = "My flash message error." resolves this.解决这个问题。 Covering the non-AJAX situation also, I went with:还涵盖了非 AJAX 情况,我选择了:

  respond_to do |format|
     format.html { flash[:error] = "error"
                   redirect_to request.referrer }
     format.js { flash.now[:error] = "error" }
   end

this way, the flash arrives (and doesn't outstay its welcome) whether the request is AJAX or not.这样,无论请求是否为 AJAX,Flash 都会到达(并且不会受到欢迎)。

I also spotted a small typo in your bootstrap-enhanced line of code:我还在引导程序增强的代码行中发现了一个小错字:

$("#flash_messages')

should be应该

$('#flash_messages')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM