Using Rails Flash Messages With Ajax
Ok, so I probably spent a little too much time working on this, but I really wanted to make it work and make it work on my own.
tl;dr
I wanted to have ajax flash messages instead of JavaScript alerts when a vote failed validation in a reddit-like clone. This is basically how I did it.
What I was trying to accomplish
For class, we are building a reddit-like clone. You can see the final solution here. One of the functionalities that we were supposed to add is the ability for the user to vote on a post or comment. We were also supposed ‘ajaxify’ the voting so that the entire page doesn’t need to be reloaded just to update a single number. That part works beautifully, as you can see in the solution. Just register and play around and you’ll see how it works. The part I didn’t like was how errors were handled (i.e. trying to vote on a post more than once.). I wanted to add the functionality for rails’ flash messages to be displayed using ajax if a vote were invalid.
How the solution does it
- The vote being created in memory in the vote function of the
posts_controller
is saved as an instance variable (@vote
). - That
@vote
instance variable then flows through to thevote.js.erb
view in the posts view folder. - There is logic inside the
vote.js.erb
view that checks if the@vote
object is valid - That logic then displays a JavaScript alert message if the
@vote
object is invalid
You can see all of this in action here.
Why I didn’t want to do it that way
- For my own sake, I didn’t want to have that kind of validity checking logic and workflow in a view instead of a controller where the rest of that kind of logic is. I wanted to follow as closely as possible to the pattern in other database-hitting methods inside the controller. For example something like this:
def update
if @post.update(post_params)
flash[:notice] = "Your post was updated!"
redirect_to post_path
else
render :edit
end
end
- I didn’t want to use flash messages for some errors/messages and JavaScript alerts for others, I wanted to be consistent.
- I wanted
vote
to be a local variable to the vote method, not an instance variable that I could accidentally mess up somewhere else. - I wanted my
vote.js.erb
view file to be as clean as possible (It’s actually just 2 lines)
$("#post_<%= @post.id %>_votes").html("<%= @post.total_votes %>");
<%= ajax_flash('#top-div') %>
The way I did it
So after a lot (I mean a LOT) of trial and error, the best way I could come up with was to create a helper method called ajax_flash
. Basically what this method does is inject the same alert div that is injected in the template from rendering the _messages.html.erb
partial. It takes the flash message and determines the correct div to put it in. The method itself returns ajax code that does 2 things:
-
Clears out the previous ajax flash message so the user can’t get a huge endless list of messages.
-
Injects the current, relevant message as the first child element of the div you specify.
If you’re more interested in exactly how I got it to work, the relevant code is in a gist here
How the method it is used
I call the method like this inside my vote.js.erb
view:
<%= ajax_flash('#top-div') %>
The method takes one argument, div_id
which needs to be a string. You could pass a class like this <%= ajax_flash('.some-class') %>
if you really wanted, but you should be passing a div_id
into it if you want it to work correctly.
The div_id
is the id of the div that you want to inject the message into as the first child element. In this project, it was a div with a class of "span12"
from twitter bootstrap that I latched onto. Just put some unique id in the div that contains the rendering of your message partial in your application layout file. Again, to see how I did that, take a look at this gist
Overall
I am very pleased with how it works and I think it fits into the project much more seamlessly than JavaScript alert messages and helps me keep the code more organized.