I’m always on the lookout for ways to improve my web applications. To that end I wanted to see if I could come up with a way to marry jQuery and Rails in a way that was both unobtrusive and a way that would degrade gracefully in a Rails 2.3.5 application. I’m looking forward to seeing how UJS with jQuery in Rails looks as of Rails 3.0, however in the meantime the method I describe below may prove to be useful.
If you want to go straight to the code it is available on Github: http://github.com/aeden/unobtrusive-jquery-example. I have also put a live demo on this up on Heroku: http://rails-jquery-unobtrusive.heroku.com.
Rather than going through all of the steps needed to set up a Rails app, the best thing to do is to follow along in the code from the example above.
First let’s take a look at the PeopleController (app/controllers/people_controller.rb):
This looks like a fairly normal RESTful controller. The one exception is the delete method, which will be used by browsers that have JavaScript turned off to provide a page that 1.) confirms that the record should be deleted and 2.) has a form that can post to the destroy method.
Next, let’s take a look at the index view:
There is a list of people with each person having a delete link next to their name. The delete link points to /person/:id/delete which will execute the delete action that I mentioned above (I’ll show you how to remove the “/delete” part of that link with JavaScript later). There is also a form for adding a new person. Note that there is no JavaScript in this page, just normal HTML elements with classes.
While we’re looking at views, let’s look at the only other view, the delete view:
This is just a simple form that confirms that they want to delete the user and provides a cancel link back to the people_url if they don’t. Note that the form tag uses the REST-ful URL for accessing the resource and sets ‘_method’ to ‘delete’ in the hidden field tag.
Finally, let’s take a look at the most complicated bit, the jQuery JavaScript. To make things easier to follow I’ll go through it piece by piece, starting with the document ready handler:
This is the jQuery way for executing code when the document has finished loading. All it does is call two methods, one for connecting up the add form and one for connecting up the delete links. To keep things organized and namespaced a bit I placed these two methods onto a JS object called ‘Actions’. First, the connectAddForm implementation:
This function finds the form element with the class “people” and attaches an event handler to the submit event. When the form is submitted an AJAX POST is made. The data type is JSON, which will call the ‘create’ action in the people controller with the accepts type set to a value that triggers Rails to render JSON as the response. The form is serialized and passed as data and when a successful response is received a bit of HTML is appended to the people list and the person name field is cleared.
Now for the connect delete links JavaScript:
Here each element that is in the people list that matches the selector a.delete and adds a click handler to it. Notice that I used the jQuery live method here, the benefit being that as new records are added, and new elements that match the selector expression are added automatically get the click handler that I define here. This saves me from the trouble of having to add the handler each time that a record is added to the page. The click handler first finds the li that it may eventually delete, displays a confirmation message to the viewer and if they confirm that they want to delete the record it will post via AJAX to the /person/:id URL with the method ‘delete’. The URL is pulled from the anchor’s href attribute and the ‘/delete’ is removed using a simple regular expression replace. Upon a successful delete the li that was found earlier is hidden using the slideToggle() function.
That’s it. You can try this in a browser either with JavaScript turned on, in which case it’ll use all of the JavaScript goodness, or you can turn of JavaScript and see the magic of graceful degradation. I hope you’ve enjoyed reading this little tutorial and if you have any suggestions on how to improve the code in the project please feel free to fork the github project, make your changes and send me a pull request. I’ll add any cool mods into the tutorial as I can.
Update: I’ve published a follow-on article that talks about changes to the code for this tutorial to handle sanitizing user input both in Rails and in JavaScript. I suggest reading it to understand XSS vulnerabilities that you need to deal with when accepting user input.





#1 by stephen on February 6, 2010 - 10:27 am
i’ve not read this yet as I have some things to do now, but I will read it later. so thanks.
#2 by Hubert Łępicki on February 7, 2010 - 4:13 pm
This is cool, and well known technique for a lot of developers. One suggestion if I can make it: you don’t need to hard-code paths like “/posts” into your JS functions. It’s usually better idea to extract those from within form action parameter. It gives you the ability to play nicely with nested resources like “/posts/4/comments/6″ etc.
#3 by Anthony on February 10, 2010 - 10:53 am
I actually tried swapping out the /posts and getting it dynamically, but something wasn’t working (and at the moment I can’t recall what). I’ll revise the post if I can get it working since you’re absolutely correct that it would be better to get it from the form action attribute.
#4 by Gudleik on February 12, 2010 - 11:47 am
Instead of doing
map.resources :people, :member => {:delete => :get}
for all your controllers, you can clean up your routes by adding a extended_resources method like this:
http://gist.github.com/302800