AJAX Components - Part 2 | Page 3
AJAX Components - Part 2
The next step beyond a behavioral component that uses HTML markup as the declaration is to create an abstraction of the HTML markup so that you can do more than just add some simple sorting or editing functionality. Using a custom-designed declaration means you can actually generate and output the HTML markup in the web browser populated with data from a client-side datasource—this will be your fully declarative client-side AJAX solution. You need to consider a few aspects when making a custom declarative AJAX component or application. For some insight into these issues, as we have already mentioned, it is always a good idea to look at existing recommendations and specifications put forward by the W3C—no matter how esoteric or generally unrealistic they might sometimes seem. It seems more often than not that just because AJAX seems shiny and new, people tend to forget that most of what they want to do has been figured out in the past in one context or another.
A good solution for databinding can be a difficult thing to achieve. By "good," we mean a solution that is flexible and provides multiple levels of indirection so that we can build complex data-bound components. To start with, let's take a quick look at a few of the data-binding solutions that have been prevalent on the web over the past decade.
Internet Explorer Databinding
Since version 4.0 came out, Internet Explorer has had client-side data-binding functionality baked into the browser. Although it is nothing too advanced, Internet Explorer does provide basic data-binding functionality by supporting two custom HTML attributes—the
DATAFLD attributes—on several different HTML elements. The
DATASRC attribute specifies a client-side datasource to which the element is bound whereas the
DATAFLD attribute specifies the specific field in the datasource to which the value of an HTML element is bound. The most common HTML element to bind to a datasource is, as in our behavioral example, the
<table> element, which is usually found bound to a repeating list of data where the list of data is repeated in
<tr> elements of the table. A data bound
<table> element might look like this:
<td> element is one that does not support the datafld attribute, we use a
<span> tag that is bound to a field from the datasource. Datasources themselves can be various structures; the most popular of which is likely the XML data island that looks like this:
Although this is a useful technology, there is still much to be desired, and it provides little more than a stop gap when it comes to building true RIAs. More recently, W3C-supported technologies such as XForms and the XML binding language (XBL) are excellent examples of thorough approaches to declarative components and databinding in the web browser.
One of the most mature options on the list is XForms.1 XForms 1.0 became a W3C recommendation in October 2003 and has not moved much beyond that. There are some real advocates of the technology, but it is yet to be championed by mainstream browsers.
In the XForms world, there are Models and controls (or Views). Models define the data and controls that are used to display the data. To bind the View to the Model, a few important declarative attributes need to be understood. First, you have single-node binding attributes. These define a binding between a form control or an action and an instance data node defined by an XPath expression. On an XForms control bound to a single data node, there can be either a
REF and a
MODEL attribute or a
BIND attribute. The
REF attributes together specify the ID of the XForms Model that is to be associated with this binding element and the XPath of the data within that Model, respectively. Alternatively, this binding information might be contained in a completely separate declaration that can be referenced using the value of the third attribute of interest that has the name
When you want to bind to a list of data rather than a single value, you can bind to a node-set rather than a single node in the Model. The
NODESET attribute, much like the
REF attribute, specifies the XPath to the nodes-set to which the control is bound. Again, either a
MODEL attribute is required to go along with the
NODESET attribute or a
BIND attribute can refer to a separate binding declaration.
Binding declaration elements, rather than just those four attributes, provide a more complete set of options for specifying how the binding to the data is to take place. The
<BIND> element connects a Model to the user interface with these additional attributes:
A final consideration is the evaluation context of the
NODESET XPath expressions. The context for evaluating the XPath expressions for data binding is derived from the ancestor nodes of the bound node. For example, setting
REF="products/product" on a parent node results in the evaluation context for XPath expressions of descendent nodes to be that same path in the specified MODEL. For a select form element, you can use the
<ITEMSET> element to define a dynamic list of values that are populated from the Model with ID
products, and the selected products are saved in the Model with ID
Because of the evaluation context, the
REF XPath values are evaluated in the context of their direct parent node, which is the root node of the
There are still more examples of declarative programming in the multitude of server or desktop languages that we could investigate such as.NET Web Forms, JavaServer Faces, Flex MXML, XUL, Laszlo, and XAML. What we can say is that most of these technologies are driven by the MVC pattern with extreme care taken to separate the Model and View. Like XForms, most also rely on XML-based data and leverage standards such as XPath and XSLT to achieve the rich functionality that you would expect from an RIA. In particular, some common threads in many of the new languages are the use of XPath in databinding expressions and the inheritance of the XPath execution context within the Model.
As mentioned, this "template" looks rather similar to what you might use if you were to generate the HTML by standard string concatenation like this.
for loop. Second, templating should make possible the creation of granular, decoupled templates, which promotes reuse and less error-prone customization. Although there might be a bit of a learning curve, both of these are well achieved by a true templating language such as XSLT, which can be a high-performance and versatile templating solution. XSLT has several advantages when it comes to the realities of implementing some templating solutions, such as good documentation (it is a W3C standard after all), granular templating, template importing capabilities—among many others. An often cited complaint of XSLT is that it is not supported in some browser. However, not only is XSLT supported in the latest versions of Internet Explorer, Firefox, Safari, and Opera, but you can also use the exact XSLT on the server to render data for user agents that do not support the technology.
A basic XSLT stylesheet looks something like this:
The values of Name and Price are retrieved based on the XML data that the template is applied to. Any
<xsl:apply-templates select="Product"/> statements can result in that template being applied. To realize the real power of XSLT, you can do things such as append a predicate to a node selection
<xsl:apply-templates select="Product[Price>10]"/> and even search entire subtrees of data just by prepending the select statement with
//. XSLT also chooses the appropriate template to apply based on the specificity of the selector— much like CSS. For example, to apply different styling to products with different prices, you can use the following XSLT:
The above templates render regular product items in a
<tr> tag for placement in a table and render products where the name is Acme Widget with a CSS class that indicates the item is a sale product.
Extensibility is a key feature of XSLT; given that the word "extensible" is in the name, you should expect as much. By using granular templates at this level, you can add or remove templates to the rendering, and the XSLT processor automatically chooses the most appropriate one. This is a deviation from other templating approaches that would likely depend on an imperative or imperative approach using an explicit
if statement to check the product name. There can be tradeoffs with execution speed and code size depending on where extensibility is important to your component or application rendering.
Three distinct cases require three different levels of templates to make these sort of interactions as fluid as possible—thus, the motivation for having as granular templates as possible. We can always depend on just the first of those templates, the initial rendering template, which would certainly achieve the goal of redisplaying edited data with the proper formatting or displaying a newly inserted row; however, this would also entail a large performance hit because rerendering all the contents of the DataGrid would make editing data slow and tedious. Instead, we want to have templates for rendering blocks of data that rely on templates for rendering single rows of data that correspondingly rely on cell rendering templates.