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.
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_controlleris saved as an instance variable (
@voteinstance variable then flows through to the
vote.js.erbview in the posts view folder.
- There is logic inside the
vote.js.erbview that checks if the
@voteobject is valid
@voteobject 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 wanted
voteto 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.erbview 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
<%= 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.
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