Here's the design I ended up implementing. I'm far from having a complete solution but I think it's a good start.
Data Model
In my case users need to be able to build a list of tasks where tasks can have different types and therefore attributes. Tasks can also embed additional objects. Similar to a form builder in a sense although I'm dealing with a deeper hierarchy of nested objects. The key here is to make sure that your back-end application only exposes objects that pertain to your application domain (in the sense of Domain Driven Design) so that your client-side code doesn't spend time refactoring data after deserializing it from a server call and before serializing it in preparation for a Save. To that extent I had to make a few changes to my server-side presentation layer but as a result I think my client-side code is cleaner and more focused on processing actual user events.
Data Serialization
I chose JSON as the data-interchange format. On the client-side I have two functions that handle the data serialization and deserialization. The implementation is pretty straight forward (in part thanks to some of the changes I made to expose domain model objects). I pasted a simplified version below. Only challenge was that the _method parameter used by Rails to handle PUT requests does not seem to work with a JSON Content-Type. See Using HTTP PUT to send JSON with Jquery and Rails 3
var todoList = {};
$.getJSON("/users/123/todolists/456.json", function(data) {
loadTodoList(data);
...
});
function loadTodoList(data) {
todoList = data.todoList;
}
function saveTodoList() {
$.ajax({
type: 'POST',
url: "/users/123/todolists/456",
data: JSON.stringify({ todoList: todoList }),
contentType: 'application/json',
dataType: 'script', // could be "json", "html" too
beforeSend: function(xhr){
xhr.setRequestHeader("X-Http-Method-Override", "put");
}
});
}
On the server-side, Rails makes it easy to handle JSON too (serialization and deserialization of JSON is performed automatically and transparently by the framework). I just overrode the to_json() method on my TodoList model to avoid passing back and forth useless data (e.g. create_at, modified_at attributes). Also had to make sure to include all nested objects when fetching my top-level object (i.e. TodoList).
# TodoListsController
def show
@todolist = TodoList.find_by_id(params[:id], :include => [:tasks, ...])
respond_to do |format|
format.json do
render :json => @todolist.to_json
end
end
end
# TodoList model
def to_json
super(:only => :name,
:include => { :tasks => { :only => [:name, :description, ...],
:include => ... }})
end
Client-side persistence
The goal here is to avoid accidentally losing user edits that haven't been saved. So far I'm directly using the HTML5 local storage (localStorage variable) but ultimately will be looking for a jQuery plugin that automatically handles falling back on cookie storage if HTML5 is not supported.
Dynamic HTML Generation
I'm relying on jQuery Template to generate HTML. The primary function of the builder is to dynamically generate HTML so this plugin comes in very handy. I've defined templates for all building blocks of my todo list model (e.g. tasks, notes, ...). The templates are invoked whenever new instances of these objects are created and need to be rendered.
I think this lays out most of the foundation. The rest is mostly hardcore Javascript to handle all user interactions with the form/todoList builder.