Using Recursive Templates

by 3 contributors:

You may recall that templates generate content recursively. After the data is generated, each result is used as the new reference point for a nested iteration of the template. This is usually used to generate content in a tree or menu. Both the RDF and XML datasource types support recursion. For example, using this XML datasource:

<people>
  <group name="Male">
    <person name="Napoleon Bonaparte"/>
    <person name="Julius Caesar"/>
    <person name="Ferdinand Magellan"/>
  </group>
  <group name="Female">
    <person name="Cleopatra"/>
    <person name="Laura Secord"/>
  </group>
</people>

We could display this data in a flat list by using the right query:

<query expr="group/person/">

Or, we could display one level for the two groups, and use another level for each person.

<groupbox type="menu" datasources="people.xml" ref="*" querytype="xml">
  <template>
    <query expr="*"/>
    <action>
      <vbox uri="?" class="indent">
        <label value="?name"/>
      </vbox>
    </action>
  </template>
</groupbox>

In this simplified example, the XPath expression just gets the list of child elements of the reference node. For the outermost iteration, a vbox is created with a child label. Since the initial reference node is the root of the XML source document, the results are two elements, one for each group element. However, a further step is done to retrieve an additional level of nodes. As each group has children itself, each result (in this case, each group) becomes the reference point for a futher iteration. The same query is executed again but using the groups generated from the previous execution of the query.

This time, the query generates a result for each person in the XML source. The content of the action body is again generated for each result, but instead of being inserted inside the outermost groupbox, this new content is inserted inside the content generated from the previous iteration. The content is always inserted directly inside the element with the uri attribute.

The result is output like the following:

<groupbox>
  ...
  <vbox id="row2" container="true" empty="false" class="indent">
    <label value="Male"/>
    <vbox id="row4" class="indent"><label value="Napoleon Bonaparte"/></vbox>
    <vbox id="row5" class="indent"><label value="Julius Caesar"/></vbox>
    <vbox id="row6" class="indent"><label value="Ferdinand Magellan"/></vbox>
  </vbox>
  <vbox id="row3" container="true" empty="false" class="indent">
    <label value="Female"/>
    <vbox id="row7" class="indent"><label value="Cleopatra"/></vbox>
    <vbox id="row8" class="indent"><label value="Laura Secord"/></vbox>
  </vbox>
</groupbox>

Note how similar content corresponding to the action body is created for both the groups as well as the people. The template builder has also added container and empty attributes to the groups. This is done with all nodes that have children to indicate that the node contains generated children as well as whether the node is empty. These hints are used for trees, but they can also be used in a stylesheet to provide a different appearance for containers with children, empty containers, as well as non-containers.

In this example, both the parent groups and child people are displayed the same. You could use multiple rules as well, in order to generate different output for each level. In this next example, an assign element is used to assign the local name of the node is to the variable ?type. In an XPath expression, the period refers to the context node. For an assign element, the context is the the result node. The local-name function retrieves the tag of the element without the namespace prefix. In this case, there isn't a namespace prefix, so the name function could be used instead.

<vbox datasources="people.xml" ref="*" querytype="xml">
  <template>
    <query expr="*">
      <assign var="?type" expr="local-name(.)"/>
    </query>
    <rule>
      <where subject="?type" rel="equals" value="group"/>
      <action>
        <groupbox uri="?">
          <caption label="?name"/>
        </groupbox>
      </action>
    </rule>
    <rule>
      <action>
        <label uri="?" value="?name"/>
      </action>
    </rule>
  </template>
</vbox>

The first rule contains a where clause which matches only those results that have a type of group. As the type is bound to the local name of the result node, this will match only the first level of results from the XML data, that is, those with the group tag. The second rule has no where conditions, so this matches all remaining results. The output for groups is a groupbox with a caption containing the name. The output for non-groups is a label.

You could further expand this process for other levels.

Disabling Recursion

The recursion on a template occurs automatically. In fact, it has occured with all of the examples so far. However, in most cases, there either aren't any children or the next iteration of the query doesn't return any results, so no output is generated. Sometimes, you will not want a template generate recursive content. You can do this by adding a flag.

<vbox datasources="people.xml" ref="*" querytype="xml" flags="dont-recurse">

Here, the flags attribute is set to dont-recurse. This disables the recursion and only generates one level of results.

Document Tags and Contributors

Contributors to this page: Sheppy, Brettz9, Enn
Last updated by: Sheppy,