Rails doesn't display flash messages after ajax call

Donovan picture Donovan · Jan 9, 2014 · Viewed 20.5k times · Source

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.

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. 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. A redirect or render in the controller alone wasn't enough.

So, to that effect, I have set a Flash notice in my controller...

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...

...
%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

Yet, no matter what I try, the Flash message does not display. Everything else works as expected except the flash message, and I haven't been able to figure out why.

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. Clearly, I'm missing something.

Here's the JS click handler (it's pretty simple):

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)

Answer

Celsian picture Celsian · Aug 4, 2014

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.

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):

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

and responds to the link as 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.

In my application.html.erb I have a partial that renders flash messages:

<%= render "layouts/flash_notices" %>

and that partial contains a div that looks like the following:

<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:

<% 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. My original view is updated with the flash message and all is right with the world.

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:

$("#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. I wanted to show there is another method. Hopefully this will help my fellow Rails newbies of the future!