Building Menus With Templates

Menus may be generated using a template in the same manner as other elements. However, unlike with other elements, the content of a menu is only generated once the menu has been opened. This means that a menu created with a template will not have any of the generated items until the user opens the menu, or a script opens the menu. This applies to menus on menubars, submenus, as well as menu-type buttons such as those with a type attribute set to menu. This allows for better performance as the entire content of a complex menu does not need to be created until the user needs to use the menu.

It is very simple to create a menu using a template. Here is an example using an XML source:

<button type="menu" datasources="people.xml" ref="*" querytype="xml">
  <template>
    <query expr="person"/>
    <action>
      <menupopup>
        <menuitem uri="?" label="?name"/>
      </menupopup>
    </action>
  </template>
</button>
<button label="Children" oncommand="alert(this.previousSibling.childNodes.length);"/>

In this example, the datasources attribute has been placed on a menu-type button. The query generates a list of the person tags within the datasource and outputs the action body for each one. The uri attribute has not been placed on the menupopup element which is the direct child of the action element but has instead been placed on the menuitem element. This causes the menupopup to only be created once. The menuitem elements however will be repeated for each result from the query. This makes sense, as we only want one menupopup to be created but multiple menuitem elements.

The second button in the example displays an alert with the number of children the template-driven button has. If you press this button before opening the menu, there will be one child (the template itself). However, after the menu has been opened, there will be two children, one is the template and the other is the generated menupopup. Note that the generated content does not get removed again when the menu is closed again; this extra feature of menus is only intended to be a performance enhancement to speed up the time it takes to display a window by avoiding extra generation that can be put off until later.

You might be wondering why we couldn't just put the datasources attribute on the menupopup instead and not have a menupopup inside the action body. You can actually do this, however, you no longer receive the performance benefit as the entire content will be generated right away. The special feature of menus to not generate content immediately applies only to the menu itself.

Generating a Recursive Menu

Menus are often generated from a template recursively. When creating recursive menus, you will need to use multiple rules, since leaf items will need to be created differently than non-leaf items. Leaf items will need to use a menuitem element whereas non-leaf items will need to use a menu element. This will involve at least two rules, although you might use other rules if you had other differences to handle. Here is an RDF example:

<button label="Houses in my Neighbourhood" type="menu"
        datasources="template-guide-streets.rdf"
        ref="http://www.xulplanet.com/rdf/myneighbourhood"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <template>
    <rule rdf:type="http://www.xulplanet.com/rdf/House">
      <menupopup>
        <menuitem uri="rdf:*" label="rdf:http://www.xulplanet.com/rdf/address"/>
      </menupopup>
    </rule>
    <rule>
      <menupopup>
        <menu uri="rdf:*" label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </menupopup>
    </rule>
  </template>
</button>

This example uses the simple RDF query syntax. The first rule matches all houses, while the second rule is used for streets. The content generated for each rule differs in only two ways. First, the menu tag is different (menuitem versus menu), and the label is taken from a different RDF predicate. At the outer level, the second rule matches the streets, so a menupopup and menu element are created. The uri attribute is on the menu element since we don't want to repeat the popup for every result. After the first level of the menu has been generated, the content will be equivalent to the following (ignoring the template related content):

<button label="Houses in my Neighbourhood" type="menu">
  <menupopup>
    <menu uri="http://www.xulplanet.com/rdf/marion" label="Marion Street"/>
    <menu uri="http://www.xulplanet.com/rdf/garden" label="Garden Avenue"/>
  </menupopup>
</button>

The inner pass through the data handles the houses. The houses match the first rule so a menupopup and menuitem element are generated and inserted inside the street content (the menu element). Again, the popup is only created once since the uri attribute is on the menuitem element. The effect is a menu with a submenu. There's nothing special about the way menus are handled -- the builder follows the same method for any type of content. However, the nature of menus can make this tricky to follow. Here is the result of the above example after both levels have been handled.

<button label="Houses in my Neighbourhood" type="menu">
  <menupopup>
    <menu uri="http://www.xulplanet.com/rdf/marion" label="Marion Street">
      <menupopup>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/16" label="16"/>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/18" label="18"/>
      </menupopup>
    </menu>
    <menu uri="http://www.xulplanet.com/rdf/garden" label="Garden Avenue">
      <menupopup>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/25" label="25"/>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/37" label="37"/>
      </menupopup>
    </menu>
  </menupopup>
</button>