AJAX Components - Part 2 | Page 3 | WebReference

AJAX Components - Part 2 | Page 3


[previous] [next]

AJAX Components - Part 2

Declarative Component

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.

When it comes to creating a declarative AJAX solution, you can look for inspiration in a number of places. From looking at the many good examples of declarative frameworks currently available from private vendors such as Microsoft (XML Application Markup Language) and Adobe (Flex MXML) as well as the W3C (XForms, Scalable Vector Graphics, XML Binding Language), two common themes appear in all of them. These themes are data binding—defining how and where data shows up in a user interface and data templating—defining how the data is formatted in the user interface. We look at some existing solutions and some ideas for custom JavaScript approaches to both of these problems.

Databinding

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 DATASRC and 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:

Because the <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.

XForms Databinding
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 MODEL and 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 BIND.

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:

  • calculate—Specifies a formula to calculate values for instance data
  • constraint—Enables the user to specify a predicate that must be evaluated for the data to considered valid
  • required—Specifies if the data required
  • readonly—Specifies if the data can be modified
  • type—Specifies a schema data-type

A final consideration is the evaluation context of the REF or 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 order.

Because of the evaluation context, the <LABEL> and <VALUE> REF XPath values are evaluated in the context of their direct parent node, which is the root node of the products Model.

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.

Templating

The second important area of building declarative components is templating of data. Templating of data is important if reuse is a priority because it should enable a high degree of customization to the component look and feel. Choosing a robust templating mechanism is a real key to creating flexible and high-performance AJAX applications and components. A few different JavaScript templating libraries are available on the web, the most popular of which is likely the JST library from TrimPath. As with many script-based templating languages (think ASP and PHP, for example), it inevitably turns out to be a mess of interspersed JavaScript code and HTML snippits—actually no different from writing JavaScript by hand. A JST-based template might look something like this:

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.

Although it might be a template by name, for all intents and purposes both of these approaches are essentially identical, and neither of them provide any of the benefits you should reap from using a templating solution. Namely, there are two primary benefits that you should expect from tem-plating. First and foremost, templating should preferably not expose user interface developers to JavaScript coding and at the very least provide a solution for applying a template to a list of items without requiring an explicit 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.

It is certainly possible, with a little effort, to replicate the functionality of XSLT in native JavaScript. Again, tradeoffs can be speed and code size; however, you do get the advantage of all their code running in the same JavaScript execution sandbox making customization and some AJAX functionality a lot easier. One instance of this is in an editable DataGrid where rendered cell values can be editing by the end user, and then subsequently the new values can be saved on the server using AJAX—without a page refresh or server post-back. If there is numeric data displayed in the DataGrid, such as the product price in our example, the price needs to be formatted according to a specific number mask to be displayed with the correct currency symbol and number formatting for the location. At first, this seems easy, but there are actually several data interactions that you need to consider. The number mask needs to be applied to the data in several cases, such as the following:

  • Initial rendering with all of the data
  • After the value is edited
  • When a new row is inserted into the DataGrid

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.


[previous] [next]

URL: