A XUL template is made up of a query and a series of rules. A query contains instructions for how to retrieve a set of data from the datasource. The exact syntax is dependent on the type of datasource used in the template. For instance, for an SQLite datasource, an SQL statement is used as the query. This will return a table of result records which are then used to generate output. For RDF and XML, the query contains a set of instructions for navigating through the RDF graph or XML document tree. A query is declared with the
query tag (new to FF3; FF2 only worked with RDF datasources and had no query tag), which you would place directly inside the <template>. The job of the query is to generate a set of output results.
The template also contains a set of rules which define content to be generated based on various conditions. An attribute substitution syntax, explained later, is used to modify the attributes of elements generated from the template, to correspond with the data for each result. Each rule is declared with a
rule tag, and you may have more than one. For each result that is generated by the query, the conditions specified within the rules are scanned, and content is generated when a rule matches. Only the first matching rule for each result from the query will apply. For example, the first rule might apply to results that represent books, while the second rule might apply only to results that are magazines. This way, different kinds of output can be created based on the specifics of the result.
In many cases, you will only have need for one rule, as you want all results to be generated in the same manner. For instance, if generating the items in a listbox, you will typically use only one rule. If only one rule is needed, the
rule element is optional. A template without any rules (or, in fact, one with a rule with no query defined) will generate output unconditionally for each result.
Template syntax outline
Here is the outline of the template syntax so far.
<vbox datasources="http://www.xulplanet.com/ds/sample.rdf" ref="http://www.xulplanet.com/rdf/A" flex="1"> <template> <query> -- query content goes here -- </query> <rule> -- rule content goes here -- </rule> <rule> -- rule content goes here -- </rule> </template> </vbox>
Query/rule compilation and lazy content generation
When the template builder starts processing, and after it has started the datasource loading, it first must compile the query and rules. This step involves working through the query rules and processing them into internal structures. Thus, changing the rule elements around dynamically doesn't affect anything. However, rebuilding the template (using the builder.rebuild method) will recompile the query and rules and reapply the template again. This means that you can change the rules using DOM methods, rebuild the template, and get different results.
Once the template builder has compiled the rules, query processing and content generation can begin. The template builder generates content lazily, that is, it processes as little as needed, and only continues when necessary. For instance, consider the example below:
<vbox datasources="http://www.xulplanet.com/ds/sample.rdf" ref="http://www.xulplanet.com/rdf/A" hidden="true"> <template> ... </template> </vbox>
The <vbox> is hidden as indicated by the 'hidden' attribute. Since any content that would be generated wouldn't be displayed anyway, the template builder doesn't do anything, putting off work until later. If you show the vbox by setting the hidden state to false, the template builder will be invoked and the content will be generated.
Does this mean that templates cannot be used inside hidden areas of the UI? No, you can still do that. Changing the hidden state of an element isn't the only way to cause content to be generated. Calling a DOM API which needs to get at the generated content will cause the template builder to generate output. For example, just calling the code like the following on the hidden vbox above will start off the template builder.
var length = vbox.childNodes.length;
This request to return the number of children of the vbox will make the template builder process the query and output content. Once done, the correct length can be returned.
All of this is transparent to the XUL developer. When the template builder decides to start generation is determined automatically, and you don't need to do anything special to get this to happen. However, there are two cases where content is not generated automatically: menus and child tree items.
Content inside a menu is not generated until the menu is opened. This makes sense since the user can't see the contents of the menu until it is open. However, it also means that the DOM API usage, such as an attempt to get the number of child nodes as above, will also not include generated items until the menu is opened. This is an important distinction. That means that you will not be able to rely on being able to retrieve the generated menu items until the menu is opened. A similar rule applies for child tree items. The children are not generated until the user presses the twisty to open the container or when a script opens a row.
Lazy generation comes in handy for menus and trees especially when dealing with recursive items. It would be rather time consuming to generate output for every item in a tree, even for those not displayed, so the template builder doesn't do so.
The template builder is even lazier. If the generated content itself contains hidden elements, those child elements will not be generated until necessary. When building content, the builder iterates down the node tree, copying and building only when needed.