Update-able DOM Components

The last few weeks I’ve been working on an Agile process management tool. We’re building it on Ruby, Rails, and Prototype. If you haven’t tried any of these, you’re missing out. The tool itself uses Prototype’s AJAX framework extensively, which brought me to the topic for this post:

What if elements in the DOM knew how to update themselves?

One of the primary components in this application is a virtualization of an XP war room. Instead of post-it notes with a few scribbles of text you get the first few words in a story’s title. The stories themselves can be dragged around the board to and from various states: planned, development, QA, complete. They can also be dragged into the next iteration, or into a project backlog.

To accomplish all this magic I needed three methods in my controller: move_future, move_next_iteration, and move_status. Each of these required different elements on the page to be updated. I had a primitive implementation working, but it contained a lot of duplication that seemed hard to factor out. For each request I had to know the story’s previous location or the story’s new location or both and how to render a number of components on the page.

The solution I settled on isn’t perfect or complete by any means, but it did help me stop repeating myself. I ended up with a single partial for rendering a “box” of stories that looked something like this:


<div class=”iteration_status” id=”iterStatus_1_1″>
   Update-able content
</div>
	
<script type=”text/javascript”>
  //<[CDATA[
    $(’iterStatus_1_1′).update = function() {
      new Ajax.Updater(’iterStatus_1_1′, ‘/iterations/story_box’, {asynchronous:true, evalScripts:true, parameters:’iteration=’ + ‘iterStatus_1_1′})
    }
  //]]>
</script>

In turn this allowed me to consolidate all my render-that-box code into one action in the controller, which you’ll see below.


def story_box
    dropbox = params[:iteration]
	
    @iteration = Iteration.find(dropbox.split(’_')[1])
    @status = Status.find(dropbox.split(’_')[2])
    stories = @iteration.stories_by_status[@status]
    render :partial => ’stories/drop_box’, :locals => {:iteration => @iteration, :status => @status, :stories => stories, :dom_id => dropbox}
end

The move_story actions are still around, but only concerned with what they should be: setting a story’s attributes and forwarding control. These rjs templates used to re-render all the necessary elements on the page, now they just ask:


page.send :record, “$(’iterStatus_#{@last_iteration.id}_#{@last_status.id}’).update();”
page.send :record, “$(’story_box’).update();”
page.send :record, “init_lbOn();”

By the way, if anyone knows of a better way to make rjs spit out verbatim javascript, I’d be pleased to find out.

The one sigificant drawback to this techinque is it fires a request for every update call. In my application this is at most three and they’re small. Additionally, the applicaiton we’re building will only be used on the local network. I’m not terribly worried.

3 Responses to “Update-able DOM Components”

  1. Dave Hoover Says:

    You can get JavaScript function calls via RJS with this:
    http://api.rubyonrails.com/classes/ActionView/Helpers/PrototypeHelper/JavaScriptGenerator/GeneratorMethods.html#M000440

    You’d have to change your code around a bit, but it’s probably worth it.

  2. Dave Hoover Says:

    Oops, the real answer to your question is this:
    http://api.rubyonrails.com/classes/ActionView/Helpers/PrototypeHelper/JavaScriptGenerator/GeneratorMethods.html#M000440

    Here it is:
    (Edited by Tyler)
    page << “Some javascript”

  3. Florian Says:

    Hi,
    I found your blog via google by accident and have to admit that youve a really interesting blog :-)
    Just saved your feed in my reader, have a nice day :)

Leave a Reply