Wednesday, April 9, 2008

How to Deliver Dynamic Javascript and HTML with an MVC Framework

What?

Some thoughts on how to organize your javascript file structure and encode your database models to json or HTML for the wire. They were inspired by an email from a guy in Germany in response to my Online Desktop demo presentation on YouTube.

Why?

MVC is a great way to organize your web apps - it makes you write good code! However, fitting snippets of html and javascript code for ajax type pulling into the picture is not necessarily obvious.

How?

I think there are a few good ways to do this, and a mix of them all is probably the right way to go. As I see it, there are three ways to deliver your js code to the client, and two ways to dynamically process and display database objects from the server on the client side:
Deliver JS
static js file inclusion (<script src=""> tag)
dynamic js file inclusion (a la dojo.require('...')
ajax request + eval on content (harder to debug and potentially unsafe)
Deliver objects and visualize them
Send json down the wire, sanitize (still a risky business), and eval (if you're using EXTjs, use it's util parseJSON method)
Send html down the wire, and use jquery's $('div').html(htmlVar) or Ext's equivelant

In Ruby on Rails, I do the first two by just including files in my /javascript/file.js directory. That's where I keep all my application code, like layout, event hooks, utility functions etc. Neatly organized into separate files, I can then easily mash them all into a single file at production time to save connections/bandwidth.

I rarely use the third option of sending javascript down the wire and running it. If you have something like GWT producing the code for you, that's fine, but otherwise I feel like it just breaks the MVC structure.

To maintain a continuous and manageble way to send my objects down the wire I tend to have two methods for each object I'm dealing with: to_html and to_json. The client then uses a query parameter to specify which format I wants the response in, and then uses setHtml(response.responseText) or eval('var obj = '+response.responseText); (with some sanitization done first).

That way I can implement the methods on the server side to very easily encode the object for display, send it, and display it however I want. A user account for example, could in ruby do:


class User < ActiveRecord::Base
def to_html
['name','email','phone'].collect do |attribute|
"
#{self.send(attribute)}
"
end
end

def to_json
{:name => name, :email => email, :phone => phone}.to_json
end
end


And on the client all I'd have to do (in jQuery) is


$('#button').click(function(user){
$.ajax('/users/view'+user.id,{
data : {
id : user.id,
format : 'html'
},
complete : function(response) {
$('#output').html(response.responseText);
}
});
});


Finally, the controller would just have to pull up the correct object, and send back the object in the right format


# Users controller
def view params
render :text => User.find(params[:id]).send("to_#{params[:format]}".to_sym)
end

No comments: