I’ve recently read that embedding ruby inside JavaScript is not a good idea.
However, in books such as David Heinemeier Hansson’s Agile Web Development with Rails, that’s exactly what it does.
If embedding ruby with JS is NOT a good a idea, then what’s the best practice for such case?
Given something as simple as this: (jQuery + ruby)
posts_controller
def create
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
format.js #will use this response to process ajax
else
format.html { render :action => "new" }
end
end
end
create.js.erb
$tr = $('<tr>');
$td1 = $('<td>').text('<%= @post.title %>');
$td2 = $('<td>').text('<%= @post.content %>');
$tr.append($td1, $td2);
$('table tbody').append($tr);
How should it be refactored to follow the “best practice” of not embedding ruby with JS?(if that’s the case)
I really need to be enlightened on this, and maybe get the concepts right because I have read that rails 3.1 will separate JS from Ruby completely through assets ?(Is this correct?)
Thanks !
RJS templates were essentially the way things were done for a long time, which is why you still see such a prevalence of the technique in tutorials. That being said, as you’ve noticed, they’re on the way out, and with good reason.
I’m sure there are many reasons why RJS templates are a very bad thing, but a big one is how tightly they couple your view JS to your view HTML. Many problems arise from this, a few being:
Lack of flexibility.
Using your bit of code as an example. What if you wanted to be able to create posts from a different view? And have a different effect? Maybe there’s no
<table>on the page at all and you just want to pop up a "success" message? Etc.What if you just wanted a data representation of your posts, but your javascript templates were written to manipulate HTML?
How do you handle all this in one
create.js.erb, which is tightly coupled to, most likely, the postsnew.html.erbtemplate?Complexity.
RJS templates operate in somewhat of a vacuum. Generated on server side, their execution is not bound to DOM events. This makes it tricky to do things like, say, update a
<form>on a page after an object is being created, as you have no frame of reference to select the appropriate<form>in the JS template (e.g.<form id="new_post_123">). There are workarounds for this, but they’re more convoluted than they need to be.By binding to a form client-side and dealing with the result, this problem is eliminated. You don’t need to find the proper form to update after a response.
With RJS you have no such binding, and you’re forced to use the known structure of the HTML to retrieve and manipulate the appropraite elements. One example of unnecessary complexity.
Regarding your question:
This is essentially true. The asset pipeline, while cool, doesn’t offer anything brand new. Asset frameworks like Sprockets and Sass, and the workflows they enable, have been around for a while. The Rails 3.1 asset pipeline just brings them into standard practice. It’s more a matter of perspective, as DHH talked about in his recent RailsConf keynote. By bringing greater organization to these assets, js files in particular, it makes them feel more like first-class citizens, so to speak, deserving of as much attention as your back-end code. As opposed to the "junk drawer" feel of
public/javascripts.As for the implementation:
You might do it something like this (although untested and a bit simplified, e.g. in Rails 3 you might use responders for this rather than a respond_to block: