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.

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.

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.

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.

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

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.

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.