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.

Example 9-1. Simple XUL template in a listbox element
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <listbox datasources="10-1.rdf" ref="urn:root" flex="1">
    <template>
      <rule>
        <conditions>
          <content uri="?jar"/>
          <triple subject="?jar"
                  predicate="http://xfly.mozdev.org/fly-rdf#types"
                  object="?types"/>
          <member container="?types" child="?type"/>
          <triple subject="?type"
                  predicate="http://xfly.mozdev.org/fly-rdf#name"
                  object="?name"/>
        </conditions>
        <action>
          <listitem uri="?type">
            <listcell label="?name"/>
</listitem>
        </action>
      </rule>
    </template>
  </listbox>
</window>

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.

Example 9-2. Basic structure of a XUL template
<listbox datasources="10-1.rdf" ref="urn:root">
  <template>
    <rule>
      <conditions>
         ...
      </conditions>
      <action>
         ...
      </action>
    </rule>
  </template>
</listbox>

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.

Table 9-1. Output of each template iteration

images

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.

Example 9-3. Hardcoded representation of generated XUL
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <listbox datasources="10-1.rdf" ref="urn:root" flex="1">
    <listitem id="rdf:#$mPgLw2">
      <listcell label="Horse"/>
    </listitem>
    <listitem id="rdf:#$nPgLw2">
      <listcell label="House"/>
    </listitem>
    <listitem id="rdf:#$oPgLw2">
      <listcell label="Fruit"/>
    </listitem>
  </listbox>
</window>

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.

Figure 9-1. DOM representation of XUL template generation

images

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.

Figure 9-2. View of XUL tree in Mozilla

images

Notes

9.2. Enhancing XUL Templates

Creating simple XUL templates can help familiarize you with the flexibility and complex design issues of a template. The RDF file created in Example 9-4 introduces the concept of nested content. A <listbox> can generate nested content from multiple containers in the RDF datasource. These multiple containers must have the same basic design to work properly, so the design must be abstracted to apply to all datasources. Example 9-5 uses the <fly:list> design to accomplish this task.

The advantage of having nested content in XUL templates is that you can organize items visually, even when those things come from different sources. Nested content allows you to form subtrees and submenus rather than long monolithic lists.

9.2.1. Nested Content Sample

The window in Figure 9-3 represents a template with nested data and styled elements. Note that the top of the content area has a standard <listbox> and a color-styled <tree> is on the bottom. The next several sections describe the creation of the XUL file in Figure 9-3.

Figure 9-3. Listbox and tree template

images

In this example, both the <tree> and the <listbox> use the same data, but different template formats. Only two columns appear in the <listbox>, for example, and the rows were created to display the color of the data's color attribute. You could as easily have styled the <tree> this way and left the <listbox> as a regular list. To display large amounts of raw data, however, <tree> is usually the best option because it's faster with big datasets, offers built-in sorting, and looks cleaner.

The <listbox> template can make the XUL seem more complicated than the content would seem to require in Figure 9-3, but a template's basic design can be similar for all types of data, which allows you to write once and apply templates to different datasets. In this case, you gain more efficiency because the RDF contributes more to the template generation than does the XUL, making template-based applications data-driven.

Example 9-4 contains the XUL for producing the tree shown in Figure 9-3. The difference between the code in Examples Example 9-2 and Example 9-9 is minimal, but the latter produces a much more impressive visual result. Remember that a XUL template and RDF produce the content you see when loading these listbox examples. No stylesheets or other enhancements are needed.

Example 9-4. XUL tree template in Figure 9-3
<listbox datasources="10-4.rdf" ref="urn:root" flex="1"
        containment="http://xfly.mozdev.org/fly-rdf#list">
    <template>
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                   predicate="http://xfly.mozdev.org/fly-rdf#list"
                     object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                   predicate="http://xfly.mozdev.org/fly-rdf#label"
                     object="?label"/>
        </conditions>
        <bindings>
          <binding subject="?listitem"
                   predicate="http://xfly.mozdev.org/fly-rdf#color"
                    object="?color"/>
          <binding subject="?listitem"
                   predicate="fly-location#location"
                   object="?location"/>
        </bindings>
        <action>
          <listitem uri="?listitem" class="?color">
<listcell label="?label" class="treecell-indent"/>
            <listcell label="?location" class="treecell-indent"/>
          </listitem>
        </action>
      </rule>
    </template>
    <listcols>
      <listcol flex="1"/>
      <listcol flex="1"/>
    </listcols>
  </listbox>

The biggest difference between Example 9-4 and earlier examples is the <bindings> section. All matching in a binding element is optional, unlike the condition content. The elements in the bindings are simply optional triples. Placing these triples in a binding affords you some flexibility when data is missing from the RDF file or when you are not certain about its contents -- such as when you create a roster but don't have all the people's addresses.

The containment attribute on the tree specifies the URI of all the containers. In this case, the container is the <fly:list> tag in the RDF. To see how such a complex-looking <listbox> can be generated from so little XUL, look at how the containers are set up in the RDF. The RDF file appears (in a reformatted and somewhat simplified form) in Example 9-5. This simplified form can help you see the structure underlying the data and how it is reused to order the data efficiently.

Example 9-5. Simplified version of 10-4 RDF data
<rdf:Description about="urn:root">
    <fly:list>
      <rdf:Seq>
        <rdf:li>
          <rdf:Description ID="House">
            <fly:label>House</fly:label>
          </rdf:Description>
          <fly:list>
            <rdf:Seq>
              <rdf:li>
                <rdf:Description about="musca_autumnalis"
                                 fly:label="Face Fly"/>
              </rdf:li>
            <rdf:Seq>
          </fly:list>
        </rdf:li>
      </rdf:Seq>
    </fly:list>
  </rdf:Description>

The RDF data in Example 9-5 demonstrates a two-level pattern of recursion: fly:list/fly:label are both reused at different levels in the RDF data. The template in Example 9-4 generates the data into a tree showing two levels, as shown in Figure 9-3.

Example 9-5 clearly shows that only fly:list and fly:label are needed to generate the template. The other data, such as color, are not mandatory because they are defined in a <binding> rather than a <triple>.

9.2.2. Using Data for Style

RDF data are used for more than containers and labels. It's possible to use RDF to define CSS classes, XUL attributes, and other arbitrary bits of XUL content. In Example 9-4, the <listitem> has a class attribute that is filled by ?color:

<listitem uri="?listitem" class="?color">
    <treecell label="?label"/>
    <treecell label="?location"/>
  </listitem>

If a stylesheet has class definitions for the same values located in the RDF, then every generated row can have its own style. Here is a simple class showing style rules defined for items of the “green” class.

.green
{
  background-color: green;
}

As shown in the earlier examples of this chapter, using <listbox> with templates generally yields flexible and simpler implementations. Trees, covered next, are not as flexible, but they can be better for raw data display, as when you have spreadsheet-like information, many columns, or other data that can be sorted.

9.2.3. Tree Template

<tree> is the best choice for displaying simple data with better visual speed, automatic sorting, and column selection capabilities. In contrast to listboxes, trees do not create a full DOM representation data when the template generates. Instead, a tree keeps data in its own database and updates its display more quickly than a listbox when the user scrolls or sorts.

The XUL tree in Example 9-6 can be compared to the listbox XUL in Example 9-4. The template design is almost exactly the same in both examples, but the elements surrounding the template in the tree -- treebody, treecol, and treecells -- are more complex and allow a precise layout by giving you more granular control over the parts of the layout. These parts include the header, the columns, and the parent-child relationships. XUL's parent element affects the presentation of the template-based data.

Example 9-6. Tree template code of Figure 9-3
<tree datasources="10-4.rdf" flex="1" ref="urn:root"
        containment="http://xfly.mozdev.org/fly-rdf#list">
    <treecols>
      <treecol id="LabelCol" flex="1"
                  sort="rdf:http://xfly.mozdev.org/fly-rdf#label"
                  class="sortDirectionIndicator" primary="true" label="Name" />
      <treecol id="LoCol" flex="1"
                  sort="rdf:http://xfly.mozdev.org/fly-rdf#location"
                  label="Location"/>
      <treecol id="ColCol" flex="1"
                  sort="rdf:http://xfly.mozdev.org/fly-rdf#color"
                  label="Color"/>
    </treecols>
    <template>
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
                  object="?label"/>
        </conditions>
        <bindings>
<binding subject="?listitem"
                   predicate="http://xfly.mozdev.org/fly-rdf#color"
                   object="?color"/>
          <binding subject="?listitem"
                   predicate=" http://xfly.mozdev.org/fly-rdf#location"
                   object="?location"/>
        </bindings>
        <action>
          <treechildren>
            <treeitem uri="?listitem">
              <treerow>
                <treecell ref="LabelCol"  label="?label"/>
                <treecell ref="LoCol" label="?location"/>
                <treecell ref="ColCol" label="?color"/>
              </treerow>
            </treeitem>
          </treechildren>
        </action>
      </rule>
    </template>
  </tree>

One major difference between this example and earlier ones is that Example 9-6 has three columns. The color data cannot be used for style in this tree scenario because trees do not support CSS data styling.

All generated data can be sorted automatically by clicking on the column headers. Besides the tree parent element in the XUL, the other main difference between this template and the one used with a listbox in Example 9-4 is the structure directly beneath <conditions>, where <content> is replaced by <treerow>.

9.2.4. Multiple Rules Tree

In Example 9-6, empty cells were left blank. Sometimes situations demand that missing data be represented by something other than whitespace, such as a special character or marker. Fortunately, multiple <rule> tags can exist in a template, as shown in Example 9-7. Alternate rule tags allow the display of missing data with other, more general rules. Using alternate tags, you can set up templates that look like conditional blocks; if the first rule is not satisfied, then the second rule is tried, followed by the third, and so on. Example 9-7 shows this structure of multiple rules.

Example 9-7. Tree template with rules
<tree datasources="10-4.rdf" flex="1" ref="urn:root"
        containment="http://xfly.mozdev.org/fly-rdf#list">
    <treecols>
      <treecol id="LabelCol" flex="1" sort="?label" label="Name"
               primary="true" />
      <treecol id="LoCol" flex="1" sort="?location" label="Location"/>
      <treecol id="ColCol" flex="1" sort="?color" label="Color"/>
    </treecols>
    <template>
      <!-- RULE 1: Row contains both a color and location. -->
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
object="?label"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#color"
                  object="?color"/>
          <triple subject="?listitem"
                  predicate="fly-location#location"
                  object="?location"/>
        </conditions>
        <action>
          <treechildren>
            <treeitem uri="?listitem">
              <treerow>
                <treecell ref="LabelCol" label="?label"/>
                <treecell ref="LoCol" label="?location"/>
                <treecell ref="ColCol" label="?color"/>
              </treerow>
            </treeitem>
          </treechildren>
        </action>
      </rule>
      <!-- RULE 2: Row contains a color and no location. -->
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
object="?label"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#color"
                  object="?color"/>
        </conditions>
        <action>
          <treechildren>
            <treeitem uri="?listitem">
              <treerow>
                <treecell ref="LabelCol" label="?label"/>
                <treecell ref="LoCol" label="-"/>
                <treecell ref="ColCol" label="?color"/>
              </treerow>
            </treeitem>
          </treechildren>
        </action>
      </rule>
      <!-- RULE 3: Row contains neither a color or location. -->
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
                  object="?label"/>
        </conditions>
        <action>
          <treechildren>
            <treeitem uri="?listitem">
              <treerow>
                <treecell ref="LabelCol" label="?label"/>
<treecell ref="LoCol" label=" "/>
                <treecell ref="ColCol" label=" "/>
              </treerow>
            </treeitem>
          </treechildren>
        </action>
      </rule>
    </template>
  </tree>

In contrast to Example 9-6, Example 9-7 moves ?location from <bindings> to <conditions> in the first <rule> in the template, making it a required match. To avoid breaking the template -- because not all objects in the RDF file have a ?location value -- you need to make a backup plan for generating this template when it encounters an object without a ?location. This backup can be a second set of more broadly defined conditions, so that objects that "fall out" of the first condition are picked up by the next. See the next section for an example of using different sets of rules.

The most important inclusions to Example 9-7 are the container="?uri" and child="?listitem" attributes on the <member>. These attributes specify which container you should apply multiple rules to, and the child is for the objects in a container that must be checked. Adding these attributes keeps the template from dying when the data doesn't meet the first rule. The second rule, which doesn't have a <triple> or <binding> to identify it, is used only when a ?location isn't present. Instead, it automatically fills in that cell with a hyphen (Figure 9-4).

Figure 9-4. Tree template with hyphen rule

images

As you can see at the top of Example 9-7, the template datasource is a file called 10-4.rdf, which contains all the data listed in Example 10-4 (in the next chapter). If you save the template listed in Example 9-7 and the RDF listed in Example 10-4, you can display the tree shown in Figure 9-4.

9.2.5. Multiple Rules Menubar

Example 9-7 is a <tree> template that contains three rules. In Example 9-8, where a <menubar> is shown with three rules, all possible menu scenarios must be covered. Table 9-2 provides a list of these scenarios. Use scenarios like this to make sure you have content that can be created for all the data you need represented.

Table 9-2. Scenarios used for building template rules

Scenario Description
Scenario 1 A <menu> has a label and contains a <menupopup>. Inside it are two <menuitem>s: ?location and ?color.

The Horn Fly, Carrion Fly, and Office Fly fall into this category.

Scenario 2 A <menu> has a label and contains a <menupopup>. Inside it is one <menuitem>: ?color.

The Common House Fly, Stable Fly, and Face Fly fall into this category.

Scenario 3 A <menu> has a label and contains a <menupopup>. Inside there is no <menuitem> (only <menus>).

Horse and House fall into this category.

The scenarios in Table 9-2 can be translated directly into three template rules. Scenario 1 would be the first rule because it uses the most content. Scenario 2 would be the second rule because it's missing only the location. Scenario 3 will be the final rule because it doesn't have a location or color.

Example 9-8. Menubar template with three rules
<menubar datasources="10-4.rdf" ref="urn:root"
        containment="http://xfly.mozdev.org/fly-rdf#list">
    <template>
      <!-- RULE 1: Menu contains both a color and location menuitem. -->
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
                  object="?label"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#color"
                  object="?color"/>
          <triple subject="?listitem"
                  predicate="fly-location#location"
                  object="?location"/>
        </conditions>
        <action>
          <menu label="?label" uri="?listitem">
            <menupopup uri="?listitem">
              <menuitem label="?color"/>
              <menuitem label="?location"/>
            </menupopup>
          </menu>
        </action>
      </rule>
      <!-- RULE 2: Menu contains only a color menuitem. -->
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
                  object="?label"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#color"
                  object="?color"/>
        </conditions>
        <action>
          <menu label="?label" uri="?listitem">
            <menupopup uri="?listitem">
              <menuitem label="?color"/>
            </menupopup>
</menu>
        </action>
      </rule>
      <!-- RULE 3: Menu contains no color or location menuitems. -->
      <!-- This applies to the main menus, shown on the menubar. -->
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                  predicate="http://xfly.mozdev.org/fly-rdf#list"
                  object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                  predicate="http://xfly.mozdev.org/fly-rdf#label"
                  object="?label"/>
        </conditions>
        <action>
          <!-- Create the menus across the menubar -->
          <menu label="?label" uri="?listitem">
            <!-- Give the menu the ability to popup content -->
            <menupopup uri="?listitem"/>
          </menu>
        </action>
      </rule>
    </template>
  </menubar>

As you can see, Example 9-8 is a long XUL section. When you create the first rule, it becomes easier, though, because the subsequent rules are just versions of the rules above them. Figure 9-5 shows how this <menubar> template draws the data in the 9-5.rdf datasource.

Figure 9-5. Menubar template with menus

images

9.3. Using Other XUL Tags for Templates

Almost any XUL element that can be used as a container can use a template to define its inner content. Example 9-9 shows a <box> used as the start for a XUL template. Templates like this can create content that doesn't look as tabular or ordered.

Example 9-9. Template implemented in a box with buttons as content
<box datasources="10-4.rdf" ref="urn:root"         containment="http://xfly.mozdev.org/
       fly-rdf#list">
    <template>
      <rule>
        <conditions>
          <content uri="?uri"/>
          <triple subject="?uri"
                   predicate="http://xfly.mozdev.org/fly-rdf#list"
                     object="?list"/>
          <member container="?list" child="?listitem"/>
          <triple subject="?listitem"
                   predicate="http://xfly.mozdev.org/fly-rdf#label"
                     object="?label"/>
        </conditions>
        <bindings>
<binding subject="?listitem"
                    predicate="http://xfly.mozdev.org/fly-rdf#color"
                      object="?color"/>
          <binding subject="?listitem"
                    predicate="fly-location#location"
                      object="?location"/>
        </bindings>
        <action>
            <vbox uri="?listitem">
              <button label="?label"/>
              <button label="?location"/>
              <button label="?color"/>
              <splitter/>
            </vbox>
        </action>
      </rule>
    </template>
  </box>

The content generated in Example 9-9 includes three <button>s and a <splitter> inside a vertical <box>. The template building process is repeated for every object in the RDF graph, and some buttons are left blank. The result is a window full of buttons for each piece of data, which may get you started making heads-up displays or panel-like applications for templates, such as flight simulators.

Once you understand the basics of templates, it is fun to see what kind of XUL you can generate from it, such as games that need to render content on the fly, spreadsheets, database front ends, or other data-driven application user interfaces.

[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.

Get Creating Applications with Mozilla now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.