Skip to content

Instantly share code, notes, and snippets.

@ordinaryzelig
Last active December 15, 2015 13:19
Show Gist options
  • Save ordinaryzelig/5266597 to your computer and use it in GitHub Desktop.
Save ordinaryzelig/5266597 to your computer and use it in GitHub Desktop.

I'm in the process of adding AJAX to a Rails app. One obstacle I have is updating the flash. I need all my AJAX requests to update the flash if my controller sets the flash with a message that I want shown on my page. So far I've converted 4 actions to AJAX, but there are at least 10 more. Customizing each action to update the flash message gets very tedious. So I need a way of DRYing it up.

For my templates, I'm using js.erb extensions (e.g. a request to /articles/create.js would render the app/views/articles/create.js.erb template). At the top of each template, I put some javascript code with some helper code:

// app/views/articles/create.js.erb
<% update_flash %>
// javascript code...

which refers to

# app/helpers/application_helper.rb
def update_flash
  [:notice, :error].each do |type|
    message = flash[type]
    concat "$('#flash_#{type}').html('message')"
  end
end

Putting it at the top of each of my .js.erb files isn't very DRY. So I wondered where a good place to put this code. My first try was to put it in my application.js file in the document ready function (I'm using jQuery) which gets run when every page is rendered. But I can't access the flash hash from here. Then, I thought I was being clever, and put it in my application layout:

<!-- app/views/layouts/application.html.erb. -->
<!-- inside <head> -->
<script type="text/javascript">
  <% update_flash %>
</script>

But this didn't work. The layout is only rendered when the layout is rendered. My ajax requests don't use the layout. This is also a problem with the first application.js solution. If you look at the response from one of the AJAX requests, it's pure javascript, no layout.

Then I got to wondering, how does Rails know not to use the application.html.erb layout? There's nothing in my controller that says "don't render the layout". Is there some code in Rails that says, "don't use layouts when the request renders javascript?" But that wouldn't make sense. And then I realized that Rails IS looking for a layout. It's just not looking for application.html.erb, because we're not using HTML. We're using javascript.

So I decided to try something. I created app/views/layouts/application.js.erb, which is the same content type that I'm using for my view templates:

// app/views/layouts/application.js.erb
alert(<%= flash[:notice]) %>)

Just as a test, in the controller, I set the flash to "Hello World":

# app/controllers/articles_controller.rb

respond_with :js

def create
  flash[:notice] = 'Hello World'
end

"Hello World"!

Now all I need is to put in my update_flash method and my action template code in there. Easy:

// app/views/layouts/application.js.erb
<% update_flash %>
<%= yield %>

Now every AJAX action template will use this new layout which will handle updating the flash whenever it's set in the controller, and then continue to execute the action's AJAX.

I never thought to use layouts in any other way than for your basic HTML layout with doctypes, banners, menus, footers, etc. This could be really helpful in DRYing up non HTML responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment