Thursday, March 22, 2007

Working with DWR


Update 6/26/2007: See also Working with DWR on the IBDOM site.

Update 5/15/2007: Meet IBDOM, our enabling framework.

Direct Web Remoting (DWR) is one of a few nifty enabling frameworks to build more interactive web applications with less pain.

Unlike most other frameworks, DWR doesn't so-much focus on User Interface. Instead, it focuses on enabling developers to expose business logic written in Java to web browser calls, through a Servlet Container such as Tomcat, Jetty or Resin.

It provides a layer that serializes data returned by a Java Method, into JavaScript JSON-esque objects, retrieved over XmlHttpRequests, and passed into JavaScript call-back functions for client-side processing.

What's interesting about this approach is that what goes over the wire is little more than just "data", versus "markup + data" ... Which doesn't matter if you're only dynamically updating a small portion of an HTML document. But if you're retrieving, say 30 listings, each of which has many fields you need to represent in a table with 7 columns, the Markup required to draw each row really starts adding-up.

Say that in Java-land, we have a SearchManager class, that has a method called "getListings(int zipcode)", that returns a collection of Listing objects. Pretending this method is "static", we could call it from another Java class as such:

listingsCollection = SearchManager.getListings(myZip);


The DWR framework exposes this very class to JavaScript, as well as all of its public methods, so calling this method from JavaScript looks very similar. But when calling it from JavaScript, we always must pass a reference to a JavaScript call-back method: it will receive the output of the method call as a parameter.

SearchManager.getListings(90254,displayListings);


Where displayListings is defined as a simple JavaScript function as such:

function displayListings(theListings) {
...
}


The question now becomes ... "what happens inside of function displayListings(theListings)" ?

The function receives a JavaScript Array, whose members are Objects whose properties match those defined back in Java in a Listing bean. We need to loop thru the Array, and for each member, update the HTML in the document, to render that listing:


listingsTable = document.getElementById("listingsTable");
for (i=0; i < theListings.length;i++) {
currentListing = theListings[i];
listingsTable.innerHTML += "" + currentListing.id + "" + currentListing.title + "
...repeat 5 more times for each column ;
}
...

ew.

.innerHTML is very simple to use, but it facilitates the mix of Markup inside scripting and that has a few disadvantages: Since it's possible to pass any string to .innerHTML, it's very possible to generate broken markup, and when broken enough, it could even crash some old version of Safari, sweet! -- not.

Further, having markup live inside JavaScript, makes it more tedious to rethink and tweak the user interface, or simply troubleshoot presentation issues. Which means more work.

Last, .innerHTML isn't part of the DOM specification, or any other spec for that matter. But all browsers do support it.

Instead of using .innerHTML, one could stick to pure DOM methods to update the listingsTable, such as:

loop ...
theRow = listingsTable.appendChild(document.createElement("tr")); theCell = theRow.appendChild(document.createElement("td"));theText = theCell.appendChild(document.createTextNode(listing.id));...repeat 6 more times for column in the row
end loop


... Sticking to DOM methods pretty-much prevents us from injecting horribly broken HTML.

But this gets very tedious, fast, and we're still building Markup inside JavaScript, leading to ... more work.

...ew.

We were looking to easily "Map JavaScript objects to HTML Elements". Imagine having reusable "templates" in your HTML document, made of any number of elements inside of which you placed "data:somePropertyName" placeholder values:

<tr class="resultRowTemplate">
<td>
<a href="data:vehicleDetailUrl">data:listingTagLine</a>
</td>
<td>
$<span>data:price</span>
</td>
<td>
<span>data:mileage</span>
</td>
<td>
<span>data:distanceFromDealer</span>
</td>
</tr>
This template was mapped to a JavaScript object with the following fields:
listing.vehicleDetailUrl
listing.tagLine
listing.price
listing.mileage
listing.distanceFromDealer


Achieving this set-up thoroughly increased the fun of working with DWR, and beyond DWR, ought to be highly useful in any situation where developers are faced with injecting JavaScript Object data into HTML documents.

We're looking to gauge interest from fellow JS/AJaX/DWR developers (Yes, YOU!), in our releasing some enabling libraries within an open-source context. Our current code is in need of serious re-factoring before any sort of public consumption. If you come across this entry, please voice your thoughts in comments. Some of us will be at BarCamp Los Angeles 3, hoping to do a little demo, code reviews about this and a few other things.

Also: We're looking for a User Interface Engineer to help out with existing similar projects, and many cooler "things" coming down the pipe.

2 comments:

Earth To Grandma said...

Great post! Thanks. I would be very interested in checking out a library written on top of DWR.

Chris Holland said...

Duly noted Tom, thanks for stopping-by. I'll post an update as soon as we have something to show.