Skip to main content

Data Binding in Laszlo - Lessons for JDNC

Posted by hansmuller on April 7, 2005 at 5:05 PM PDT

For the past two months or so, I've been working with some of the

JDNC
developers on the databinding problem. After some false starts, the
approach we've taken is to define "data aware" components and some
special encapsulation classes for relational data. The overall goal is
to make forms and master/detail applications relatively easy to build
by automating most of the donkey work involved in interconnecting data
sources with Swing GUI components and their attendant data and
selection models. All in all not a terribly novel quest, however it's
an important one and it's long overdue.

Data binding is an old problem and there are plenty of worthy examples
to learn from. The support in ADO.NET and the newer version of that
in Avalon certainly qualifies, although the complexity of Microsoft's
solution should serve as a warning about the dangers of trading
simplicity for generality. The

JGoodies data binding system
, which is based on the SmallTalk ValueModel idea, is another
fine example. The Laszlo platform, which is popular among Flash
aficionados and can boast some very beautiful demos, also supports
basic data binding. I'd written a short review of Laszlo's databinding
support recently and the JDNC developers with whom I'd been working
suggested publishing it here.

In case you're already bored I'll provide the punchline first. We've
observed that data binding systems that bury the natural syntax for
specifying bindings with abstractions can be more trouble than they're
worth. If I'm binding to JavaBeans then strings using the usual
"bean.property" expression language notation are a nice way to specify bindings.
Similarly, if you're binding to an XML document, then an XPath expression
is a natural way to define a component's data source. Laszlo's data
binding system is designed just for XML data and they employ
an XPath subset for specifying bindings. Here's how.

What follows is based on a quick study of the
data binding part of the Laszlo
developer documentation,

chapters 28-30
.
The Laszlo designers had about as much flexibility as anyone could
want, since they designed a new "language" (an XML schema) for
defining applications that targeted Macromedia's Flash player.

Laszlo applications are defined by an XML document. Trivially simple
GUIs survive the encoding and remain simple:

<canvas width="500" height="350">
    <text>Hello World</text>
</canvas>

Complex GUIs are probably more compact than a Java version however in
my (biased) opinion, one ends up writing too much
cryptic JavaScript/unix-shell style line noise like:

<tree datapath="*" text="$path{'@name'}" 
    isleaf="${this.datapath.xpathQuery('@type') == 'file'}"/>

The Laszlo platform includes a modest number of basic GUI controls; behavior
is defined with JavaScript. There are a handful of very primitive
layout managers however if the documentation is any indication,
absolute layout is the norm. Platform look and feel fidelity is
not a goal.

Laszlo supports XML data binding with XPath (a small subset)
expressions. The XML data can be embedded in the application source
code, which is good for examples, or it can be loaded from a URL. The
binding system works nicely for simple things however once the world
of "hello world" is left behind, Laszlo gets ugly in a hurry. There
doesn't appear to be any support for master/detail applications with
their pesky connections to selection. Apparently master/detail
relationships must be defined at the level of writing "onClick"
handlers. Updating a relational database is a similarly
do-it-yourself operation.

That said, the basic XML data binding support is tidy enough,
particularly if you're familiar with XPath syntax. To bind
to an XML document you have to give it a name, and to do that you
use the dataset tag:

<canvas>
    <dataset name="customerData">
        <customers>
    <customer firstName="Fred" lastName="Mertz"/>
    <customer firstName="Ethel" lastName="Mertz"/>
        </customers>
    </dataset>
    <simplelayout axis="x"/>
    <text datapath="customerData:/customers/customer[1]/@firstName"/>
    <text datapath="customerData:/customers/customer[2]/@firstName"/>
</canvas>

The dataset tag has a "src" attribute, like the HTML anchor
or image tags, whose value is a URL. There's adequate support
for programatically reconfiguring and and reloading a dataset
and one can write handlers that get notified each time the
dataset has been completely loaded.

Thanks to the "simplelayout" (like AWT flow) layout tag, this GUI just
displays "Fred Ethel". The data binding for each text component is
specified by the "datapath" attribute which names the dataset and, on
the RHS of the ":", the XPath expression of the data that the
component it bound to.

Note: if you're an XPath novice as I am, you might find the datapath
expressions in the example a little bit confusing. They look like
filesystem paths (and they're called "paths") but their semantics are
subtly different. They're really patterns that match XML tag
names and properties of XML elements and attributes. The latter are
enclosed in square brackets, e.g. "[1]" means the element whose
one-based index (relative to its siblings) is 1. So the XPath
"/customers" means: all of the elements whose tag is "customers" and
"/customers/customer[1]" selects the first child whose tag is
"customer", and "/customers/customer[1].@firstName" selects its
"firstName" attribute. It took me a while to stop trying to write
"/customers[1]/@firstName". I'm over that now.

The Laszlo generic container class is called "view" and you can
bind it to data and then use relative data binding paths on
the components it contains. This example produces the same
output as the previous one:

<canvas>
    <dataset name="customerData">
        <customers>
    <customer firstName="Fred" lastName="Mertz"/>
    <customer firstName="Ethel" lastName="Mertz"/>
        </customers>
    </dataset>
    <view datapath="customerData:/customers/">
      <simplelayout axis="x"/>
      <text datapath="customer[1]/@firstName"/>
      <text datapath="customer[2]/@firstName"/>
    </view>
</canvas>

Where Laszlo data binding gets interesting and ugly is
in producing tables. There's no explicit table construct,
you just bind a view to a list of elements and the
GUI components contained by the view are "replicated",
once for each list element.

<canvas>
    <dataset name="customerData">
        <customers>
    <customer firstName="Fred" lastName="Mertz"/>
    <customer firstName="Ethel" lastName="Mertz"/>
        </customers>
    </dataset>
    <simplelayout/>
    <view datapath="customerData:/customers/customer">
      <simplelayout axis="x"/>
      <text datapath="@firstName"/>
      <text datapath="@lastName"/>
    </view>
</canvas>

The XML above produces a GUI with four elements, like this:

Fred Mertz
Ethel Mertz

In other words, we're not binding firstName and lastName to columns in
a special table component. What we've got is just short hand for
creating one row of textfields for each customer element in the
dataset. There is no selection support and watch out for large data
sets. To deal with the latter you can try and configure the special
"replication manager" which provides pools of components
that can be reused for rows, and the "onclone" JavaScript callback
that alerts you when a view has been replicated, and, well, yecch.

There are several other interesting features of the Laszlo databinding
system that I don't think are worth diving into here. For example one
can bind component attributes to XPath expressions (but be prepared to
think hard about when those bindings are evaluated). There are
"datapointers" which can be used to create bindings that get moved
around with methods like datapointer.selectNext().

What's good about all of this is that, assuming you've got XML data,
you can bind to that data using moderately intuitive XPath
expressions. Returning to JDNC and given our canonical Customers,
Orders, and Parts example, and a quick and dirty schema, you could
write the bindings for the three tables (roughly) like this:

customersTable.setDataPath("/customers/customer");
ordersTable.setDataPath("/customers/customer[@selected]/order");
partsTable.setDataPath("/customers/customer[@selected]/order[@selected]/part");

<customers>
    <customer firstName="Fred" lastName="Mertz">
        <order id="o1">
    <part id="p1"/>
    <part id="p2"/>
        </order>
        <order id="o2">
    <part id="p3"/>
        </order>
    </customer>
    <customer firstName="Ethel" lastName="Mertz"/>
        <order id="o3">
    <part id="p4"/>
    <part id="p5"/>
        </order>
    </customer>
</customers>

Here I've created a synthetic attribute called "selected" that's
implicitly defined for all tags. If it's present then the
corresponding XML DOM node is selected. The implication is that the
binding engine that interpreted this binding would the customers
JTable's selection model at run time. What's nice about this is that
the developer gets to define the bindings using the same syntax as the
(XML) data they're binding to.

Related Topics >>