Chapter 37. Data Access and Binding

Table of Contents

1. Preview of databinding
1.1. Types of data objects
1.2. Two ways to interact with data
2. OpenLaszlo datasets and data nodes
2.1. The src attribute
3. Ways to include data
3.1. Embedded Data
3.2. Included Data
3.3. HTTP Data
3.4. Dataset Scope
3.5. ondata, onerror and ontimeout event
3.6. POST support
3.7. HTTP Request and Response headers
3.8. Cookies
3.9. Datasources
4. AJAX API
4.1. Using XMLHTTPRequest() in SOLO applications
5. Datapointers
6. Accessing data
6.1. Datasets
7. Simple binding
7.1. Overriding applyData
7.2. Attribute bindings using $path
7.3. The .data property
7.4. Using ondata to process updates
8. Manipulating datapointers
8.1. rerunxpath
8.2. Forcing visibility of datamapped views
8.3. Update timing (order of data initialization)
8.4. Iterator methods
9. Data processing
9.1. Using setPointer() to bind data
9.2. Controlling a datapath

This chapter describes various methods of tying XML data structures into your LZX application. See Chapter 36, Data, XML, and XPath for discussion of some of the concepts used in this chapter. For a gentle introduction to databinding and manipulation in OpenLaszlo applications, you may start with the tutorial: Chapter 11, Introduction to Databinding

1. Preview of databinding

By "databinding" we mean the automatic association of a value in an XML data structure with an element in the LZX view hierarchy of the application. This chapter explores various aspects of databinding and manipulation in OpenLaszlo applications. Before going into specific details, we'll start with a conceptual overview of how data is represented in LZX applications, and the APIs for manipulating it.

1.1. Types of data objects

A dataset (LzDataset) is two things:

  • Firstly, it is the client side store for XML data. It's where a single XML document lives in an OpenLaszlo application.

  • Secondly, it's the mechanism by which OpenLaszlo applications make HTTP GET or POST requests.

An LzDataElement is the LZX class that represents a single XML data tag in OpenLaszlo applications. LzDataElements are usually kept in a dataset, although data-bound views can get pointers to them even if they are not in a dataset. Inside of a dataset, LzDataElements are linked in a tree-like structure, but that doesn't mean to say that an LzDataElement must go inside of a dataset.

LzDataElement is a subclass of LzDataNode, as is LzDataText..

Finally, note that a dataset is a subclass of LzDataElement, which means that all of the methods which work on LzDataElements also work on LzDataSets, although the usual method of manipulating datasets is with datapaths and datapointers, as explained below.

1.2. Two ways to interact with data

As we have said, all data in OpenLaszlo applications is in XML format. There are two related but distinct ways of using and manipulating that data in OpenLaszlo applications; that is, there two API models:

  • The DOM model—in which the APIs allow you to directly manipulate elements of a Document Object Model using DOM conventions.

  • The DataPointer model—in which the APIs allow you to position a logical cursor within the dataset using XPATH syntax

These two categories of APIs have similar functionality with large areas of overlap. However, there are some things that can only be done (or can best be done) using one specific approach (and not the other). This means that in many situations there are two logically distinct ways to achieve the same result. Learning to master data manipulation in LZX is a matter of becoming fluent in both approaches and knowing when to use each.

1.2.1. LzDataNodes and the DOM APIs

The Document Object Model is, according to the W3C specification, "a platform- and language-neutral interface that will allow programs and scripts to dynamically access and update the content, structure and style of documents." In the LZX context, the "document" is that LzDatanode.

LzDataNode is the base class for the classes that represent LZX's hierarchical data format. An LzDataNode comprises LzDataElements. An LzDataElement represents a node in a hierarchical dataset. An LzDataElement can contain other LzDataElements, or LzDataText, which represents a text node. More advanced data manipulation in OpenLaszlo applications employ the various methods on the LzDataElement class, such as appendChild(), getNextSibling(), and so forth. These classes can only be created in script, not by tags. For tag-based data manipulation, use <dataset> and the related concepts of datapointers and datapaths.

1.2.2. Datapointers and Datapaths

In addition to LzDataNodes, which can only be manipulated in script, LZX includes the notions of <datapath> and <datapointer> , which provide a convenient, tag-based mechanism for typical data manipulation. By using datapointers to move through the data, you control the behavior of views that are bound to that data.

2. OpenLaszlo datasets and data nodes

Data in OpenLaszlo applications can be declared with a tag, or built up using procedural (script) APIs. The script APIs operate on LzDataNodes.

All declaratively-declared data in OpenLaszlo applications is contained within one or more datasets. The content of a dataset is an XML fragment with a single root node, but without the XML declaration. A given dataset usually represents a single conceptual set that may or may not be modified or reloaded during the execution of the application.

You declare a dataset in your application using the <dataset> tag. The name of the dataset is used in the datapath attribute of a view, as will be explained below.

Datasets can be embedded directly in applications, constructed at runtime, or procured from remote servers. A dataset may be declared on the canvas, in which case it is visible to the entire application, or it may be declared within a class, in which case it is visible to the members of that class.

To embed a dataset directly in an OpenLaszlo application, you use the <dataset> tag as below. In this example, you can get access to the given dataset by referring to canvas.shelf.

Example 37.1. Embedding data in an OpenLaszlo application

<canvas>
  <dataset name="shelf">
    <bookshelf>
      <book binding="paperback">
        <title>Acts of the Apostles</title>
        <author>John F.X. Sundman </author>
        <publisher>Rosalita Associates </publisher>
        <price>15.00</price>
        <year>1999</year>
        <category>thriller</category>
        <rating>4.5 </rating>
      </book>
      <book binding="casebound">
        <title>Shock</title>
        <author>Robin Cook </author>
        <publisher>Putnam </publisher>
        <price>24.95</price>
        <year>2001</year>
        <category>thriller</category>
        <rating>3.5 </rating>
      </book>
      <book binding="paperback">
        <title>Cheap Complex Devices</title>
        <editor>John Compton Sundman </editor>
        <publisher>Rosalita Associates </publisher>
        <price>11.00</price>
        <year>2002</year>
        <category>metafiction</category>
        <rating>5.0 </rating>
      </book>
    </bookshelf>  
  </dataset>
</canvas>

This style of dataset inclusion is called local data in that the data is included locally in the application, rather than being retrieved from a remote data source or web service. Data can be included from a remote source by specifying the src attribute as follows:

Example 37.2. Dataset from a remote source

<canvas height="400">
  <dataset name="menu" src="http://www.w3schools.com/xml/simple.xml" request="true"/>
  <simplelayout axis="y"/>
  <button onclick="t.setAttribute('text', menu.serialize())">Show XML data </button>
  <inputtext multiline="true" width="${canvas.width}" bgcolor="0xa0a0a0" id="t" height="300"/>
</canvas>
edit

In this example the OpenLaszlo application, when it starts up, makes a HTTP request for the url, http://www.w3schools.com/xml/simple.xml and populates the dataset named menu with the XML returned. You can click the button to see the serialized contents of the dataset.

2.1. The src attribute

The src attribute should be a well-formed URL that points to the back-end data source that will produce the data. This may be an absolute or relative URL. (All requests made for relative URLs are relative to the application's URL.) The URL may point to a static XML file or a server-side processor (such as JSP, ASP, PHP, and so on) that produces XML data.

The src attribute of the <dataset> element specifies whether the data is compiled into the application or fetched at runtime:

  • If the src attribute is a URL, the value of the dataset is the XML data that a request to the URL named by the src attribute returns when the application is run.

  • If the src attribute is a pathname, the value of the dataset is the content of the XML file that the pathname refers to, and is compiled into the application.

  • If the src attribute is not present, the value of the dataset is the content of the <dataset> element.

The data within a dataset is accessed using a <datapointer> or a instance of one of its subclasses.

A dataset is an instantiation of the LzDataset class. An LzDataset is a JavaScript object that provides a Document Object Model (DOM) API for accessing, manipulating, and creating XML elements and attributes in memory. These APIs are discussed in Chapter 37, Data Access and Binding. The dataset also has APIs that pertain to data transport.

2.1.1. Interpreting a datapath

The datapath of the <text> tag binds it to the data.

Datapaths use XPath attributes to navigate through the XML data. So the name of the dataset to use goes before the colon myData:, followed by the nodes, separated by forward slashes (/). The square brackets provide a (one-based) space to enter which sibling node we want. [1] is implied, so the above example could be rewritten without any "[1]"s.

The /text() path segment is unnecessary with the datapath attribute.

So far we've used the <text> tag in conjunction with a single datapath. If we wanted to present tabular information, this would mean each text element would need its own datapath, and would be cumbersome and difficult to write. Instead let's make a quick table, by giving a <view> a datapath:

Example 37.3. Assigning a datapath to a view

<canvas height="80" width="500">
  <dataset name="myData">
    <myXML>
      <person show="simpsons">
        <firstName>Homer</firstName>
        <lastName>Simpson</lastName>
      </person>
      <person show="simpsons">
        <firstName>Marge</firstName>
        <lastName>Simpson</lastName>
      </person>
      <person show="simpsons">
        <firstName>Montgomery</firstName>
        <lastName>Burns</lastName>
      </person>
    </myXML>
  </dataset>

  <view name="rowOfData" datapath="myData:/myXML[1]/person[1]">
    <simplelayout axis="x"/>
    <text datapath="firstName/text()"/> 
    <text datapath="lastName/text()"/> 
    <text datapath="@show"/>
  </view>
</canvas>
edit

The datapath of the entire rowOfData view has now become Homer's person node. The child elements of rowOfData inherit this, so their datapaths can be referenced relatively.

2.1.2. Multiple rows of data

In the above example we used a single rowOfData node. Next, we shall use a range of all of the nodes:

Example 37.4. Range of nodes

<canvas height="80" width="500">
  <dataset name="myData">
    <myXML>
        <person show="simpsons">
          <firstName>Homer</firstName>
          <lastName>Simpson</lastName>
        </person>
        <person show="simpsons">
          <firstName>Marge</firstName>
          <lastName>Simpson</lastName>
        </person>
        <person show="simpsons">
          <firstName>Montgomery</firstName>
          <lastName>Burns</lastName>
        </person>
      </myXML>
  </dataset>

  <view name="myTable">
    <simplelayout axis="y"/>
    <view name="rowOfData" datapath="myData:/myXML[1]/person">
      <simplelayout axis="x"/>
      <text datapath="firstName/text()"/> 
      <text datapath="lastName/text()"/> 
      <text datapath="@show"/>
    </view>
  </view>
</canvas>
edit

Whichever tag contains the datapath attribute will get repeated as often as is necessary.

Remember that datapaths bind themselves to a view, so if the data changes, so will the view.

3. Ways to include data

The source for a dataset may be anything that returns XML, including sources elsewhere on the web. For instance, the source may be a URL for a .jsp or .php program that generates XML data "on the fly." This is a typical architecture for OpenLaszlo applications. The table below highlights ways of categorizing datasets according to where the data comes from and how it is integrated into the application.

How is it included? When is it loaded? Syntax
Embedded Compile-time
<dataset name="myData">
  <myXML>
     <!-- ... other XML tags ... -->
  </myXML>
</dataset>
Included Compile-time
<dataset name="myData" src="myXMLDoc.xml"/>
HTTP data Runtime
<dataset name="myData" request="true" 
         type="http" src="myXMLDoc.xml" />

3.1. Embedded Data

Embedded data is XML between the <dataset> tags. When the OpenLaszlo Compiler compiles the application, the data is bound into it. The data can still be changed after the application runs.

3.2. Included Data

Included data is essentially the same as embedded data, except that the XML itself is kept in a separate file. The size of the initial download will be the same as with embedded data.

It is locally referenced via the filesystem, so it can be placed in other directories. Included data is static.

3.3. HTTP Data

Remote data goes over HTTP, which means it can (but doesn't have to) be dynamic. If it is static, then the only difference between it and included or embedded data is that it is downloaded after the application loads. The type="http" attribute tells the OpenLaszlo Server that this is an HTTP request. The requests can be either GET or POST.

There are several points at which the client makes requests for the data:

  • The client will request the data as soon as the app loads if the dataset's request attribute is true.

  • The client will also request the data every time the querystring or base URL of the dataset changes (using the setQueryString() or setURL() respectively) methods of the LzHTTPDataset object.

  • When the dataset's doRequest() method gets called.

In the table above, we referenced a file locally (myXMLDoc.xml), but we could have done it absolutely, or we could have hit a server-side script (PHP, ASP, JSP or some CGI) that returned an XML document. We could add the query string to the <dataset> tag:

<dataset name="myData"
         src="http://www.myServer.com/cgi-bin/myXMLDoc.cgi?return=addresses"/>

The type="http" attribute gets implied when the src attribute contains "http://".

[Note] Note

You do not have to worry about speed of the Flash Player's XML parser when using the OpenLaszlo Server.

3.4. Dataset Scope

If specified on the canvas, datasets are visible to and accessible by the entire application. Datasets can also be local to a class.

  • Datasets will automatically name themselves localdata if a name is not specified

  • Local datapath syntax is datapath="local:reference.to.dataset.relative.to.parent:/path"

  • The name of the dataset can be omitted from the datapath if the dataset name is the default 'localdata', e.g. 'local:classroot:/' can be used instead of 'local:classroot.localdata:/' for a dataset named localdata in the classroot

Here is a simple program that illustrates use of local datasets (the file tests/data, a sample XML file is included in this directory also.)

Example 37.5. local datasets

<canvas width="100%" height="200">
  <debug fontsize="12"/>

   <view layout="axis: y">
    <dataset name="ds" src="http://..."/>
    <text datapath="this.ds:/record/text()"/>
   </view>

  <class name="myclass" layout="axis: y">
    <dataset name="ds" src="http://..."/>
    <text datapath="this.ds:/record/text()"/>
  </class>
  <myclass/>
  <myclass/>


    <dataset name="gdata" src="resources/testdata.xml"/>

  <simplelayout spacing="2"/>

  <view name="nodatanoname" layout="axis: y" bgcolor="#cccccc">
    <handler name="onclick">
        Debug.write(this.localdata);
    </handler>
    <dataset/>
    <text>empty local dataset with no name</text>
  </view>

  <view name="nodata" layout="axis: y" bgcolor="#cccccc">
    <handler name="onclick">
        Debug.write(this.lds);
    </handler>
    <dataset name="lds"/>
    <text>empty local dataset</text>
  </view>

  <view name="somedata" layout="axis: y" bgcolor="#cccccc">
    <handler name="onclick">
        Debug.write(this.lds);
    </handler>
    <dataset name="lds">
        <foo>bar</foo>
    </dataset>
    <text>local dataset</text>
    <handler reference="lds" name="oninit"><![CDATA[
      
      Debug.write("somedata test data loaded", this);
      if (this.lds.serialize() != '<lds><foo>bar</foo></lds>') {
      Debug.error("somedata serialized data does not match expected value");
      }
      
    ]]></handler>
  </view>

  <view name="filedata" layout="axis: y" bgcolor="#cccccc">
    <handler name="onclick">
        Debug.write(this.lds);
    </handler>
    <dataset name="lds" src="resources/testdata.xml"/>
    <text>local dataset compiled in from external file</text>
    <handler reference="lds" name="oninit"><![CDATA[
      
      Debug.write("filedata test data loaded", this);
      if (this.lds.serialize() != '<lds><persons><person id="1"><firstName>Dan</firstName><lastName>McGowan</lastName><modifyDate>3/25/05</modifyDate><address code="ML" id="1"><line1>2210 North 184th Street</line1><line2/><city>Shoreline</city></address></person><person id="2"><firstName>Barry</firstName><lastName>Bonds</lastName><modifyDate>3/25/05</modifyDate></person><person id="3"><firstName>Jeff</firstName><lastName>Beck</lastName><modifyDate>3/25/05</modifyDate></person></persons></lds>') {
      Debug.error("filedata serialized data does not match expected value");
      }
      
    ]]></handler>
  </view>

  <view name="remotedata" layout="axis: y" bgcolor="#cccccc">
    <handler name="onclick">
        Debug.write(this.lds);
    </handler>
    <dataset name="lds" src="resources/testdata.xml" type="http" request="true"/>
    <text>local dataset loaded at runtime</text>
    <text datapath="local:parent.lds:/persons/person/firstName/text()" onclick="Debug.write(this.datapath)"/>
    <handler reference="lds" name="ondata"><![CDATA[
      
      Debug.write("remotedata test data loaded", this);
      if (this.lds.serialize() != '<lds><persons><person id="1"><firstName>Dan</firstName><lastName>McGowan</lastName><modifyDate>3/25/05</modifyDate><address id="1" code="ML"><line1>2210 North 184th Street</line1><line2/><city>Shoreline</city></address></person><person id="2"><firstName>Barry</firstName><lastName>Bonds</lastName><modifyDate>3/25/05</modifyDate></person><person id="3"><firstName>Jeff</firstName><lastName>Beck</lastName><modifyDate>3/25/05</modifyDate></person></persons></lds>') {
      Debug.error("remotedata serialized data does not match expected value");
      }
      
    ]]></handler>
  </view>

  <view name="remotedatarelative" layout="axis: y" bgcolor="#cccccc" datapath="local:lds:/persons/" visible="true">
    <handler name="onclick">
        Debug.write(this.lds);
        this.datapath.setXPath(this.datapath.xpath);
    </handler>
    <dataset name="lds" src="resources/testdata.xml" type="http" request="true"/>
    <text>local dataset loaded at runtime and relative datapath - datapath doesn't resolve because dataset doesn't exist yet.  click to reparse xpath</text>
    <text datapath="person/firstName/text()" onclick="Debug.write(this.datapath)"/>
  </view>

  <class name="localdatatest">
    <dataset/>
    <view datapath="local:classroot:/">
      <simplelayout/>
      <handler name="onclick">
        this.datapath.addNode('child', 'Click to remove this node', {});
      </handler>
      <text>Click to add a node to my local dataset</text>
      <text x="10" datapath="child/text()" onclick="this.datapath.deleteNode();"/>
    </view>
  </class>

  <class name="redlocaldatatest" extends="localdatatest" bgcolor="red"/>

  <localdatatest/>
  <localdatatest/>
  <redlocaldatatest/>

</canvas>
edit

3.5. ondata, onerror and ontimeout event

When the application's LzDataset receives the data, the ondata event is sent. In the case that an error occured in communicating with the back-end (which may be proxied by the OpenLaszlo Server in proxied applications, or direct, in SOLO applications), an onerror event is sent instead. And, if there is a timeout (currently hard-coded at 30 seconds) in communicating with the back end, an ontimeout event is sent. The OpenLaszlo Runtime guarentees that each request generates exactly one of ondata, onerror, or ontimeout.

3.6. POST support

Datasets support both HTTP GET and POST methods for communicating with the OpenLaszlo Server and back-end servers. The default is GET but this can be changed with the LzDataset.setQueryType() API. In general, requests with large query parameters should be sent via POST.

3.7. HTTP Request and Response headers

In general, the OpenLaszlo Server proxies HTTP request and response headers to and from the back-end. However, certain headers are specifically omitted or modified.

Note that response headers are not available to SOLO applications.

3.8. Cookies

The OpenLaszlo Server proxies all "Cookie" request headers and all "Set-Cookie" response headers. Because of the domain name restrictions on cookies, the OpenLaszlo Server can only properly proxy these cookie headers when the back-end host is in the same domain (or a subdomain) or the OpenLaszlo host. For more on this topic, see Chapter 44, Cookies and Sessions

3.9. Datasources

Underlying each dataset that communicates over HTTP is an LzDatasource object. This object abstracts all protocol specific communication. In general, you do not need to use the datasource object yourself, except for very rare situations.

For example, one situation is when you are running the OpenLaszlo Server secure port on something other than 443; the only place you can specify the secure port is on the datasource.

4. AJAX API

<XMLHTTPRequest> implements XMLHttpRequest as specified by the what-wg consortium. Basically, this class allows you to fetch XML data from a URL, and so it is essentially equivalent to the LzDataset API (or the <dataset> tag.) It is provided as a convenience to developers who are familiar with its syntax from its use in AJAX applications.

Here is an example of the XMLHTTPRequest class.

Example 37.6. XMLHTTPRequest

<canvas width="1400" height="600" debug="true">
  <debug width="400" height="300" fontsize="12" x="400"/>

  <include href="rpc/ajax.lzx"/>

  <script>
    
    
    var req = null;

    function processReqChange() {
        Debug.write("processReqChange: req.readyState", req.readyState);
        // only if req shows "loaded"
        if (req.readyState == 4) {
            // only if "OK"
            if (req.status == 200) {
                Debug.write("req.status", req.status);
                Debug.write("req.responseText:", req.responseText);
                Debug.write("req.responseXML:", req.responseXML);
                Debug.write("req.getAllResponseHeaders:", req.getAllResponseHeaders());
            } else {
                Debug.write("There was a problem retrieving the XML data:\n" +
                            req.statusText);
            }
        }
    }

    
    function loadXMLDoc(url) {
        // branch for native XMLHttpRequest object
        req = new lz.XMLHttpRequest();
        req.onreadystatechange = processReqChange;
        req.open("GET", url, true);
        req.setRequestHeader('X-Test', 'one');
        req.setRequestHeader('X-Test', 'two');
        req.send(null);
    }
    


    
  </script>
    <simplelayout spacing="4"/>
    <edittext id="in1">example.xml</edittext>
    <button onclick="loadXMLDoc(in1.getText())">Load Data</button>

    <button onclick="loadXMLDoc('badurl')">Test Error Handling, this should fail</button>

</canvas>
edit

4.1. Using XMLHTTPRequest() in SOLO applications

In SOLO applications, the XMLHTTPRequest class does provide one capability that is not currently available from datasets; that is, you can get the raw text of the XML as a string, before it is parsed. You do this using the responseText() method. This capability is only available in SOLO applications.

Note that by accessing a URL in this way you can fetch data that is not XML, which may come in handy in some situations. However, since LZX is predicated on the XML data model, in general you shouldn't expect to be using this technique very much.

Also, in SOLO deployed applications, the XMLHTTPRequest class departs from the what-wg specification in these ways:

  • HTTP headers are not settable

  • response headers are not accessible

  • you cannot send raw POST data

  • you cannot send repeated query args in a POST using LoadVars

  • Username/password HTTP Auth args to send() are not supported.

5. Datapointers

We introduced datapaths first, which are extensions of datapointers, but they can become cumbersome. A datapointer points to just one place of the dataset at a time, but can be moved around -- you can have multiple datapointers, each pointing to a different part of a dataset.

Datapointers are not bound to views like datapaths are, but they do have a place in the view hierarchy—that is, they "know about" parents and children.

You will use a datapointer when you need to operate on the data in some way. For example, using the same format of data as in the previous examples, say you wanted to find all the people who were in the South Park show:

Example 37.7. Manipulating datapointers

<canvas height="180" width="500" debug="true">
  <dataset name="myData" src="resources/myShowData.xml"/>
  
  <datapointer xpath="myData:/" ondata="processData()">
    <method name="processData">
      this.selectChild(2); 
      do {
        if (this.xpathQuery( '@show' ) == 'south park') {
            Debug.write(this.xpathQuery('lastName/text()'));
        }
      } while (this.selectNext()); 
    </method>
  </datapointer>
</canvas>
edit

For brevity's sake, we are writing to the debugger, and we are including the data from a local file.

The selectChild(2) method call selects the <myXML> node, then the South Park <person> node -- it selects the second-depth node because of the depth argument "2" we passed it (otherwise it would default to 1.

The selectNext method call returns true as long as an XML node was successfully selected (i.e. until there aren't any more). We exploit this by using it in a do while loop, so that the same iteration occurs for every <person> node.

We could also have given the <datapointer> onerror and ontimeout event handlers to capture any problems.

6. Accessing data

6.1. Datasets

A <dataset> provides a way to encapsulate arbitrary XML data in an OpenLaszlo application. Depending on the source of the data, datasets can be static or dynamic. When a dataset is explicitly declared with type="http", the value of its src is interpreted as an URL and the dataset is populated with data at runtime. If the src attribute is absent, the data it represents is expected to be contained within the <dataset> tags, and thus also compiled into the application.

When we say that HTTP datasets are dynamic, we mean that you can repopulate them programmatically by calling the doRequest() method of the dataset object, or if the request attribute is set to true, by changing the URL of the dataset when one of the setSrc(), setQueryString(), or setQueryParam() methods is called.

6.1.1. Globally Visible Datasets

When a dataset is defined as an immediate child of <canvas> or <library> , it can be referenced anywhere in the code through the datasets property of canvas, i.e. canvas.datasets['mydset'], or simply by its name (it is globally visible):

Example 37.8. Explicitly defined datasets

<canvas debug="true" height="200">
  <debug height="150"/>
  <dataset name="mydset" src="http:?lzt=xml"/>
  
  <dataset name="week">
    <day>Sunday</day>
    <day>Monday</day>
    <day>Tuesday</day>
    <day>Wednesday</day>
    <day>Thursday</day>
    <day>Friday</day>
  </dataset>
  
  <script>
    Debug.write(mydset);
    Debug.write(canvas.datasets['mydset']);
    Debug.write(week)
  </script>
  </canvas>
edit

6.1.2. Datasets created at runtime

Datasets can also be created at runtime in script by calling the constructor for the LzDataset: var dset = new LzDataset(null, {name: 'mydset'}). The first argument to the constructor is the dataset's parent node, which is the <datasource> that encloses this dataset; this parameter is allowed to be null — in this case a datasource will be created implicitly.

7. Simple binding

The LZX event system allows you to insert custom data handling into the application as needed. This is typically done by overriding the applyData() method of the databound node, by providing a handler for the ondata event on the datapointer or datapath, or by defining a $path constraint on an expression-type attribute and processing changes to the attribute's value with the onattribute_name handler.

7.1. Overriding applyData

The applyData() method is called on any node that is declared with a datapath that matches a terminal selector, such as text() or @attribute when the data it matches is changed. The argument passed to the method is the string the data represents. Use the ondata event if the node is bound to a datapath that matches a data node (see below).

Example 37.9. Overriding applyData

<canvas height="150">
  <dataset name="colors">
    <value>red</value>
    <value>green</value>
    <value>olive</value>
    <value>yellow</value>
    <value>blue</value>
    <value>teal</value>
  </dataset>

  <simplelayout spacing="10"/>
  <view name="swatch" width="200" height="30" datapath="colors:/value[1]/text()">
    <method name="applyData" args="v">
      setAttribute('bgcolor', eval(v))
      display.setAttribute('text', v)
    </method>
  </view>
  <text name="display" resize="true"/>

  <button text="Change view color">
    <attribute name="ind" value="$once{1}"/>
    <handler name="onclick">
      if (++ind == 7) ind = 1
      swatch.setAttribute('datapath', 'colors:/value[' + ind + ']/text()')
    </handler>
  </button>
</canvas>
edit

7.2. Attribute bindings using $path

Attributes of a node can be bound to data explicitly by using the $path{} constraint syntax. The expression inside the curly brackets must evaluate to a string, which is interpreted as a relative XPath expression.

7.2.1. Absolute paths

If you need to use an absolute path in the expression, you could instead constrain the attribute to the result of an xpathQuery() call: visible="dp.xpathQuery('mydset:/record/row[1]/@visible')". A limitation of the $path{} constraint is that the expression it contains is evaluated only at the initialization time, that is, an expression such as $path{'mynode[' + i + ']/@attr'} will behave like a $once{} constraint.

$path bindings are two-way, so calling updateData() on a node's datapath will store the current value for that attribute back in the dataset.

Example 37.10. $path constratint bindings

<canvas height="150">
  <dataset name="sizes">
    <value>200</value>
    <value>150</value>
    <value>100</value>
  </dataset>
  
  <button text="Shrink me" datapath="sizes:/value[1]"> 
    <attribute name="width" value="$path{'text()'}"/>
    <handler name="onclick">
      if (!datapath.selectNext()) this.setAttribute('text', 'Done')
    </handler>
  </button>
  <button y="40" text="Stretch me" datapath="sizes:/value[1]"> 
    <attribute name="width" value="$path{'text()'}"/>
    <handler name="onclick">
            datapath.setNodeText(Number(datapath.getNodeText()) + 20)
    </handler>
  </button>
</canvas>
edit

7.2.2. Attribute values by $path constraint

The dataset is basically an array. Assigning a node's attribute value by $path constraint, and then using the XPath expression myData:/myXML/person -- which gathers all the "person" nodes -- results in replication of the node as many times as necessary to correspond with each item in the dataset.

In this example, there are three items in the dataset, so the box node replicates three times.

Example 37.11. Assigning an attribute value by $path constraint

<canvas debug="true">

    <dataset name="boxes">
        <boxes>
         <box color="0xFF0000"/>
         <box color="0x00FF00"/>
         <box color="0x0000FF"/>
         </boxes>
    </dataset>

<simplelayout axis="x" spacing="10"/>

<class name="coloredbox" height="50" width="50" bgcolor="$path{'@color'}"/>

<coloredbox datapath="boxes:/boxes/box"/>

</canvas>
edit

7.2.3. $path values in calculations

JavaScript provides easy casting from string to numeric data types. The $path{...} syntax is defined to return a string, which can be used to assign a value to an attribute. You can combine attributes to preform calculations based on strings returned from a $path{...} inquiry on a dataset.

Say, for example that you had a dataset that contained temperatures in Fahrenheit that you wished to convert to Centigrade. You would have to create an intermediate attribute that binds to the (possibly replicated) path constraint and then bind your text field to a calculation on that attribute. Something like:

Example 37.12. calculations on $path{} values

  <attribute name='intermediate' value="$path{'degf'}" \>
  <attribute name='text' value="${Number(intermediate) * 5 / 9}" />

7.3. The .data property

The data property is a shorthand way of accessing data referenced by a datapointer or a datapath. For convenience, a datamapped node gets its data property set to that of the datapath it is bound to. In the example below, the color view changes its properties as the data field to which they are constrained follows the "order" attribute of the nodes in the dataset. Note that the data is a string value of the attribute; this is the case when the XPath matches an operator. The datapath of the enclosing view, however, refers to entire node in the dataset, and its data property contains an instance of LzDataNode that the XPath references. This is evident from the debugger output.

Example 37.13. Using the data property

<canvas height="150" debug="true">
  <debug x="150"/>
  <dataset name="onion">
    <layer order="1"><layer><layer>core</layer></layer></layer>
  </dataset>

  <view datapath="onion:/layer">
     <simplelayout spacing="5"/>
     <view width="${100 / this.data}" height="${100 / this.data}" bgcolor="0x09d055" datapath="@order" opacity="${Math.min(1, this.data / 3)}"/>
    
    <button text="Peel next layer">
      <handler name="onclick">
        with (parent.datapath) {
          Debug.write(data)
          if (!selectChild()) this.setAttribute('enabled', false) 
          else setNodeAttribute('order', Number(p.parentNode.attributes['order']) + 1)
         }
      </handler>
    </button>
  </view>
</canvas>