Integrating jQuery and Rails: use unobtrusive Javascript instead of view helpers
The jQuery Way to apply behavior to a document is to add it unobtrusively via the $(document).ready() function, ideally in an external script file… unlike the Rails Way, which is to add behavior to individual elements via inline Javascript, using the built-in view helpers.
The markup generated by these helpers is obtrusive, and often repetitive. For example, using the built-in link_to_remote helper like this:
<%= link_to_remote post.title, :url => post_path(post) %>
<% end -%>
…generates this HTML:
<a onclick="new Ajax.Request(‘/posts/2′, {asynchronous:true, evalScripts:true}); return false;" href="#">Post 2</a>
<a onclick="new Ajax.Request(‘/posts/3′, {asynchronous:true, evalScripts:true}); return false;" href="#">Post 3</a>
… which is obtrusive, non-degradable and non-DRY. Of course, one can code in an unobtrusive, degradable, DRY way with Prototype; that’s not the issue. But in looking for ways to integrate jQuery with Rails, we shouldn’t be attempting to clone all of the Rails Prototype helpers (which enable less-than-ideal coding practices) for jQuery.
Instead, we should be writing degradable HTML in our views, and adding behavior unobtrusively via Javascript.
For example, I added the following functions for Ajaxifying standard links and forms to my application.js:
$(function() {
$("a.rjs").click( function() {
$.ajax({
url: this.href,
dataType: "script",
beforeSend: function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");}
});
return false;
});
// requires jQuery.form plugin
$("form.rjs").ajaxForm({
dataType: ’script’,
beforeSend: function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");},
resetForm: true
});
});
(See my previous post about jQuery Ajax + Rails for information about how jQuery and RJS can work together.)
And now in my views, I can just add a class of “rjs” to any link or form that I want to Ajaxify:
<% for post in @posts -%>
<%= link_to post.title, post_path(post), :class => ‘rjs’ %>
<% end -%>
<h3>Add a Post</h3>
<% form_for :post, Post.new, :url => posts_path, :html => { :class => "rjs" } do |f| -%>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= submit_tag "Create" %>
<% end -%>
… and it works: unobtrusive, degradable and DRY.
6 Comments