Chapter 9. XUL Templates

XUL templates are dynamically generated XUL elements and groups of XUL elements. They are often used to render lists and tables that display mutable, frequently updated data, such as your Inbox, your list of bookmarks, and user profiles. A XUL template can be used to create something as simple as a list of menu items, as you will see here, but it can also be used in much more exciting ways, as shown at the end of this chapter. You should consider using a XUL template instead of XUL when you want to create an interface that displays data, such as a roster of names, when the set of data is very large, when the data may change frequently, or when you create a display that you want to use for different sets of data.

RDF, the format for data that goes into templates, is described in detail in Chapter 10. The actual data used to build the template examples is displayed in Examples Example 10-1 and Example 10-4. However, this chapter precedes the RDF chapter because templates are much easier to understand than RDF. Extending on the XUL programming done in Chapters Chapter 2 and Chapter 3, templates are a practical application of RDF data. They can also help you understand the abstract concepts introduced in Chapter 10.

9.1. Understanding XUL Templates

By defining special rules and applying them to data stored in RDF files, XUL templates build user interfaces dynamically. A XUL template consists of a set of special tags inside a XUL element -- often <listbox>, <menu>, or <tree> elements that match data in an RDF datasource. A XUL template is defined in a regular XUL file and may appear anywhere regular XUL content can be placed.

The template defines rules for filling out the parent elements with the associated RDF data. Example 9-1 shows how to get a <listbox> in XUL to display RDF file contents. A template like this could display data stored in a RDF file that, because it's so long, complex, or ephemeral, shouldn't be hardcoded into XUL list elements. The data that comes from RDF and goes into a template should be anything that doesn't directly relate to the user interface.

Because the template is built to match the RDF data, different parts of the template in Example 9-1 correspond to parts of the RDF file used as the datasource. Obviously, you need to know about the data's organization -- the "graph" created by the data -- to build effective templates for it. However, once you create the rules, you can apply them to very large sets of data, which is one of the benefits of using templates in the interface.

As you can see in Example 9-1, rules typically comprise most of a template's definition. The next several sections break down Example 9-1 to help you understand the parts of a XUL template.

9.1.1. Basic template structure

Example 9-2 shows the template's basic structure. In this case, the data that meets the conditions defined in the conditions element is rendered by the XUL elements defined in the actions element, allowing the translation of RDF data into XUL elements.

In the first lines of the XUL template, a <template> is defined within a <listbox> element, which is a simple container for templates in XUL:

XUL:
  <listbox datasources="10-1.rdf" ref="urn:root" flex="1">
RDF: 
  <rdf:Description about="urn:root">

The <listbox> gains two special attributes when it contains a <template>. The datasources attribute specifies the RDF file's location. The ref attribute is the starting point in that RDF-based data for the template processing, which is equivalent to the about attribute of the root node in the actual RDF file. The ref attribute tells the template where to begin reading the data in the RDF file, and the about attribute in the RDF data file specifies where its own beginning is. In this case, the RDF and XUL starting point is the root of the data. Note that you do not need to define a template at the base of an RDF data file: an RDF file may have several avenues of information (e.g., different groups of bookmarks) and your template may render only one group or some portion of all of the RDF file data.

9.1.1.1. Template rule conditions

XUL:
  <template>
    <rule>
      <conditions> 
RDF:
  <!-- no equivalent -->

The <template> and <rule> tags set up the template. The template's rule element defines conditions that must be met for the template to render the referenced data. A common condition in a template, for example, is that an element be of a particular type or have an attribute set to a certain value. The conditions in Example 9-2 render this content (10-1.rdf) if it defines a types property and gives individual child elements as types.

Applying template rules to a datasource drives the dynamic creation of the template-based UI. You can imagine a template going through data and selecting only the bits of data that match, based on matching rules, and then rendering that selected data into XUL (again based on rules defined in the template itself).

Generated values from the RDF are stored in variables that can be used throughout the template. They are represented by the values inside the attributes beginning with a ?. When you create variables in a template once, you can use them wherever you need them in the template. In Example 9-1, the ?type variable is created as a child of types in the conditions block, is used again, and is then used a third time in the action block to describe the element that should be rendered in the template:

XUL:
  <content uri="?jar"/>
  <triple subject="?jar"
          predicate="http://xfly.mozdev.org/fly-rdf#types"
          object="?types"/>
RDF:
  ... about="urn:root" ...
  ... xmlns:fly="http://xfly.mozdev.org/fly-rdf#" ...
  </fly:types>

The <content> tag signifies the root of the template rule. The uri attribute value is automatically filled with urn:root from the listbox ref attribute, which originates from the RDF about attribute on the first resource. This value is now stored in the ?jar variable. Assigning variables in a template for use elsewhere in the template is an essential part of template-building in XUL, as it is in programming languages that work with data.

A <triple> is a test on a subject and predicate. When triples match the subject and predicate in the RDF, their object value is produced. In this case, the container is the object result ?types, which holds individual ?type nodes. Each one of these is drawn as a <listitem>.

The <member> element initiates a loop-like effect. When the template builds, this effect exposes the container so it can read through all the objects and add them to the template. In essence, ?type holds three different values throughout the template generation: [1]

XUL:
  <member container="?types" child="?type"/>
RDF:
  <fly:types> 
    <rdf:Bag>
      <rdf:li>
        <rdf:Description ...

To finish the template conditions, one more <triple> correlates with the literal's value.

XUL:
  <triple subject="?type"
          predicate="http://xfly.mozdev.org/fly-rdf#name"
          object="?name"/>
RDF:
  ... xmlns:fly="http://xfly.mozdev.org/fly-rdf#" ...
  <rdf:li>
    <rdf:Description fly:name="Horse"/>

Like ?type, ?name holds three different values during its lifetime, and "Horse" will be the first value generated from the RDF.

The <conditions> part of the template extracts the data from the RDF graph, as in our graphical examples. It makes the data available in variable-like objects; those objects can be used in the template's <action>, demonstrated in the next section.

9.1.1.2. Template rule actions

XUL elements that are used to build content from data matched by the template conditions are placed in the <action> element in a template rule. The <listbox> is the most popular way to display this data because all of its child elements fall neatly into place inside the template. However, you can use any XUL element that supports the type of tabular display required by the data (e.g., <tree>, <groupbox>, and <menu>).

  <action>
    <listitem uri="?type">
      <listcell label="?name"/>
    </listitem>
  </action>

For the RDF content to be displayed, it needs a parent/children team to define and fill in the values where needed. The parent, ?type, is used as a point of reference three times during its lifetime by objects directly in the container. The template generates ?name into the three literal children, as shown in Table 9-1.

Directly inside the <action> element is the first XUL element that gets repeated, the <listitem>. This element must have the uri attribute with the container's object variable, which in Example 9-2 is ?type. This variable establishes the root of the content -- a point of reference in the template for any children below that point.

Once the container elements are matched to the <listitem>, ?name can be used in any attribute on any tag below it. In the previous example code, the <listcell> label shows the value of ?name. Interesting implementations can result from the use of variables to hold values for attributes like class, which is often used to define style rules for elements. This implementation is demonstrated in the section Section 9.2.2, later in this chapter

Example 9-3 shows what a generated template looks like as hardcoded XUL.

It's beneficial to see how this document is translated into a DOM tree using Mozilla's DOM Inspector tool, with which the structure is presented in a view that makes it easy to follow. Figure 9-1 shows how the template tree nodes are generated into an actual tree. To use this tool, select "DOM Inspector" from the Tools > Web Development menu in Mozilla. If you have the template displayed in an open browser window, you can load it in the DOM Inspector by selecting File > Inspect a Window and choosing it from the list.

In Figure 9-1, you can see how the <listitem> was generated three times from the template. Interestingly, the generated code doesn't replace the original template, but is appended to the <tree> as another tree row.

Finally, Figure 9-2 shows what the actual XUL file looks like when loaded. If you save the template in Example 9-1 to a file called 9-1.xul, save the RDF data in Example 10-1 to a file called 10-1.rdf (which the template looks for by name in the same directory), and then load the template into the browser, you ought to see something very similar.

Notes

[1]

An rdf:Bag is a type of RDF container, but when you use the <member> tag, you do not have to specify whether the container is of the type Alt, Bag, or Sequence (see "nsIRDFContainer" in Chapter 10 for more details on working with containers in RDF). A template can only build the values sequentially out of a container, no matter what type it is. Thus, using an rdf:Seq or rdf:Alt element produces the same visual output in these Mozilla templates.