Neil's Place

July 9, 2005

7:03 PM How Templates Work XXII - Using Multiple Rules

All of the examples shown so far have used only a single rule. The builder supports the use of multiple rules as well. This involves using additional <rule> elements after the first. There are three main reasons for using multiple rules. First, to generate different content when different criteria are met, second, to apply different content for child elements created during template recursion, and third, just to generate additional results that will be merged with the first rule. We'll see examples of each of these techniques. A multiple rule template looks like the following:

<hbox id="photosList" datasources="template-guide-photos3.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos">
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?photo"/>
        <triple subject="?photo"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="Canal"/>
      </conditions>
      <action>
        <button uri="?photo" image="?photo" label="View" orient="vertical"/>
      </action>
    </rule>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?photo"/>
      </conditions>
      <action>
        <image uri="?photo" src="?photo"/>
      </action>
    </rule>
  </template>
</hbox>

This template contains two rules, the first contains a <triple> which matches only the photo with a title of "Canal". The second rule doesn't contain such a triple and will match all three of the photos. It the first rule was used by itself, only one result would match. If the second rule was used by itself, three results would match. When used together in this example, the results are combined and only three results are shown. However, you will probably notice that the one photo that matches the first rule has appeared differently that the others. In fact, the content for this photo is that of the first rule with the button, whereas the content for the other photos is that of the second rule with the normal images.

This demonstrates the first style of using multiple rules, to generate different content under different circumstances. This is a very useful and commonly used technique since it allows for more complex content to be displayed. For instance, the Firefox bookmarks toolbar displays folders in a different manner than regular bookmarks. This is done by using multiple rules, one for folders and another for bookmarks. Actually, there are more than two rules used in bookmarks, since there are also separators, submenus, and livemarks to deal with.

We already know that a rule generates a set of results, possibly filtered based on the rule's conditions. There's no magic to the way in which the template builder processes multiple rules. It just takes the results generated from the first rule, adds the results for the second rule, adds the results for the third rule, and so on. Here are the results that would be generated by the first rule above, before any bindings are applied:

(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg,

Then, the builder adds the three results generated from the second rule:

(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg,
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/palace.jpg)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/obelisk.jpg)

So four possible results are available, one from the first rule and three from the second. However, the example shows that only content for three results are generated. What happened to the fourth result?

This is where the useful aspect of multiple rules comes in. Note that two of the results above are actually for the same photo (canal.jpg). The template builder removes any duplicate items before generating content. It does this by only allowing the match for the earliest rule. That is, the canal.jpg generated by the second rule is removed, since an earlier rule (the first rule) already generated a match for that result.

One important distinction is that the determination of duplicates is only based on the member variable, in this case the ?photo variable. It doesn't matter whether other variables are the same or not.

If you look at the example again, you might notice that the canal photo that matches the first rule has appeared in-between the other two photos, even though those photos are generated from the second rule. The builder hasn't put all the matches for the first rule before the matches for the second rule. In fact, the order is the same as the examples that only use one rule. Compare the multiple rule example with an earlier example that used only a single rule. The photos have appeared in the same order in both cases.

This is because the builder notices that the photos are in an RDF Seq in the datasource and arranges them in the order they appear in the Seq. This and other automated sorting done by the template builder is a fairly complicated process that will be discussed in more detail later.

Comments ( 5 )

July 8, 2005

7:41 PM How Templates Work XXI - Containment Properties

So far, we've seen how simple rules can generate results from the children of an RDF container. However, simple rules may also iterate over a single predicate pointing out of a resource, rather than use the children. This allows you to iterate over a non-container. To do this, use the containment attribute on the root node, set to the predicate to iterate over.

Let's look back at the very first example again. It doesn't have any containers, but we may want to iterate over the relatedItem predicate using the simple rule syntax. We can add the containment attribute to do this.

<vbox datasources="template-guide-ex1.rdf"
         ref="http://www.xulplanet.com/rdf/A"
         containment="http://www.xulplanet.com/rdf/relatedItem">
  <template>
    <rule>
      <label uri="rdf:*" value="rdf:*"/>
    </rule>
  </template>
</vbox>

Instead of iterating over a container, this example iterates over a specific predicate. This attribute is useful when the RDF data is structured in such a way that an RDF container isn't used.

The builder treats the predicate in the containment attribute as an indicator that an element is a container. If the starting node (or ref) has that predicate as one of its triples in the RDF data, the builder will use it in addition to checking if it is a container. Note the distinction here. The containment attribute doesn't replace the existing container checking. If the resource "http://www.xulplanet.com/rdf/A" was also an RDF Seq with some children, then those children would also be added to the results. This next example demonstrates this. The only difference between this and the previous example is a a couple of additional lines added to the RDF/XML:

<rdf:Seq rdf:about="http://www.xulplanet.com/rdf/A">
  <rdf:li rdf:resource="http://www.xulplanet.com/rdf/E"/>
  <rdf:li rdf:resource="http://www.xulplanet.com/rdf/F"/>
</rdf:Seq>

The effect is that there are five results instead of three. Three generated via the use of the containment attribute, and two generated because the starting resource is a container with two children. Effectively, the containment attribute allows you to specify additional predicates that provide children.

You can specify multiple predicates in the containment attribute by separating them with spaces. Each will be applied in sequence.

The containment attribute also applies to <member> tests when using the extended template syntax. That is, in addition to iterating over the children, it will iterate over the predicates listed in the containment attribute. For example, the following is equivalent to the previous example, except that the full rule syntax is used.

<vbox datasources="template-guide-ex3.rdf"
         ref="http://www.xulplanet.com/rdf/A"
         containment="http://www.xulplanet.com/rdf/relatedItem">
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?child"/>
      </conditions>
      <action>
        <label uri="?child" value="?child"/>
      </action>
    </rule>
  </template>
</vbox>

Try this example. What happens is that the builder generates additional possible values for the ?child variable, so it creates an additional result for each one.

Comments ( 6 )

July 6, 2005

5:49 PM How Templates Work XX - Simple Rule Conditions

Simple rules also use a simpler way of handling conditions to filter out unwanted results. It allows you to filter for results that have certain predicates with specific values. For instance, you may filter results that have a certain category or country. You can only filter on predicates pointing out of the member resource. That is, you cannot filter on predicates pointing into the member resource nor can you cannot filter based on predicates pointing out of other resources.

The conditions are specified using attributes on the <rule> element. The namespace of the attribute and the attribute name combine to form the predicate you wish to filter by and the value of the attribute will be the value to filter by. Any results that match the criteria are accepted and any that do not match are rejected. You can use multiple attributes to filter by several criteria. In this case, all of the criteria must match for the result to be accepted.

For example, to filter for photos with a specific country:

<hbox id="photosList" datasources="template-guide-photos4.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos"
      xmlns:r="http://www.xulplanet.com/rdf/">
  <template>
    <rule r:country="http://www.daml.org/2001/09/countries/iso#IT">
      <vbox class="box-padded" uri="rdf:*">
        <image src="rdf:*"/>
        <label value="rdf:http://purl.org/dc/elements/1.1/title"/>
      </vbox>
    </rule>
  </template>
</hbox>

This example shows how a single attribute may be used to filter for only those results that have a country set to "http://www.daml.org/2001/09/countries/iso#IT". Photos that do not have this country will not have any content generated for them. The attribute has a namespace that, when expanded, becomes "http://www.xulplanet.com/rdf/country". This syntax is much like how predicates are the specifed in the RDF/XML.

This form of attribute syntax on a rule is equivalent to a <triple> in the extended syntax. In fact, the builder will internally convert the attribute into the same compiled form that would be used for a <triple>. The equivalent triple for the above example might be:

<triple subject="?photo"
        predicate="http://www.xulplanet.com/rdf/country"
        object="http://www.daml.org/2001/09/countries/iso#IT"/>

The simple rule syntax is limited in the kind of filtering it can do, however it is often sufficient for many purposes. Naturally, if you want to set a filter dynamically, as the photos example does with a menulist, you will need to set the attribute on the <rule> element and rebuild the template.

function applyFilter(country)
{
  var rule = document.getElementById("filterRule");
  if (country){
    rule.setAttributeNS("http://www.xulplanet.com/rdf/", "country", country);
  }
  else {
    rule.removeAttributeNS("http://www.xulplanet.com/rdf/", "country");
  }
  document.getElementById("photosList").builder.rebuild();
}

This version of the applyFilter function only needs to set or remove the attribute on the rule as necessary. Note that the namespace aware functions (with the suffix NS) need to be used to set or remove attributes with namespaces. This example assumes that the rule has been given an id of "filterRule". Note that the menulist itself must use the extended syntax since it doesn't iterate over the children of a resource.

As mentioned earlier, you can filter by several criteria by using multiple attributes. For instance:

<rule id="filterRule"
      dc:title="Obelisk"
      r:country="http://www.daml.org/2001/09/countries/iso#IT">

This rule filters on two criteria which must both match. Rules will apply all the attributes as conditions except for the id attribute, as well as some special attributes, which are ignored. These special attributes are useful when handling multiple rules, which will be the subject of a later topic.

Comments ( 4 )

July 2, 2005

3:00 PM How Templates Work XIX - Simple Rule Syntax

When iterating over the children of an RDF container, there is a simpler rule syntax which may used. It may also be used when iterating over only one predicate. Simple RDF graph navigation such as this is common, so the simpler syntax is usually used in this situation since it avoids extra tags, although the simple syntax is not more or less efficient, at least when a single rule is involved. A simple rule is equivalent to a rule with only the content tag and a member tag, as well as optionally a set of triples from the child node. At it's simplest, the simple rule syntax is equivalent to the following conditions:

<conditions>
  <content uri="?start"/>
  <member container="?start" child="?photo"/>
</conditions>

The template builder uses the simple rule syntax whenever a template rule does not have a <conditions> element. If a <conditions> element is present, the full syntax is used instead. In the simple syntax, the builder will use the default conditions shown above, although the variable names are randomly generated.

Since the photos list iterates over the children of a container, we can use the simple syntax. Here is what it might look like:

<rule>
  <vbox class="box-padded" uri="rdf:*">
    <image src="rdf:*"/>
    <label value="rdf:http://purl.org/dc/elements/1.1/title"/>
  </vbox>
</rule>

There is no <conditions> or <action> element used. Instead, the builder uses a default set of conditions, and the action is placed directly inside the <rule> element. You can see that this code is much simpler than the full syntax. To understand how this works, let's examine how the builder processes a simple rule. First, it evaluates the default conditions as above, except that no variables are used, or at least, not ones that are used externally. In reality, the builder could be considered to convert a simple rule into an extended rule, so that it can be processed in the same way. After the default conditions are evaluated, the data network will look something like this:

(?1 = http://www.xulplanet.com/rdf/myphotos,
 ?2 = http://www.xulplanet.com/ndeakin/images/t/palace.jpg)
(?1 = http://www.xulplanet.com/rdf/myphotos,
 ?2 = http://www.xulplanet.com/ndeakin/images/t/canal.jpg)
(?1 = http://www.xulplanet.com/rdf/myphotos,
 ?2 = http://www.xulplanet.com/ndeakin/images/t/obselisk.jpg)

The numbers are used here for the variable names, but they are really just randomly generated so you can't refer to them in any way. The member data, or results are held in the variable represented by '?2'. Three matches have been found so the content will be duplicated three times.

There are several attributes in the content that have values prefixed with 'rdf:'. Since you can't declare variables in the simple syntax, a different way is used to grab RDF data. The 'rdf:' prefix means look up a predicate pointing out of the node for that result (the child or member node). For example, the label's value attribute has a value of "rdf:http://purl.org/dc/elements/1.1/title". This means look up the value of the predicate "http://purl.org/dc/elements/1.1/title" pointing out of the current result. Of course, it should be obvious that this causes the value attribute to be set to the photo's title.

You can get other predicates in the same way, just by using the predicate uri and prefixing it with 'rdf:'. You can also combine two in one attribute by separating them with a space or a caret (^) just as you can with the full syntax. For example:

<label value="rdf:http://purl.org/dc/elements/1.1/title http://www.xulplanet.com/rdf/country"/>

The 'rdf:' form is really equivalent to the <binding> tag in the full syntax. The subject of the binding is the result member resource, the predicate is the string that comes after 'rdf:' and the object isn't necessary as the value is replaced directly in the attribute. As with bindings, the value is not required to match, so the data doesn't have to exist in the RDF graph. If one photo doesn't have a title, it will be replaced with an empty string.

The special value 'rdf:*' means the uri of the result resource, rather than referring to a predicate. It would be equivalent to ?photo variable in this example if the full syntax was used. You can see in the example that the value 'rdf:*' is used in two places, the uri attribute on the hbox and as the src attribute for the image.

Here is the photos example using the simple syntax. The filtering isn't shown in this example. It is still possible to do filtering using simple rules and we'll look at this next.

Comments ( 7 )

July 1, 2005

8:08 PM How Templates Work XVIII - Static Content

Creating an menuitem in the filter list to show all items is fairly easy. This item doesn't need to be generated from the datasource. Instead it will be what is called static content in a template. The static content is any content that is a sibling of the <template> element. This content is displayed as is without affecting the generated content from the template.

<menulist datasources="template-guide-photos4.rdf"
           ref="http://www.daml.org/2001/09/countries/country-ont#Country"
           oncommand="applyFilter(event.target.value);">
  <menupopup>
     <menuitem label="All"/>
  </menupopup>
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <triple subject="?country"
                predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
                object="?start"/>
        <triple subject="?country"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="?countrytitle"/>
      </conditions>
      <action>
        <menupopup>
          <menuitem uri="?country" label="?countrytitle" value="?country"/>
        </menupopup>
      </action>
    </rule>
  </template>
</menulist>

The only difference between the previous example and this one is that the <menulist> element has some additional content added before the template. The content will be displayed just like it would be as if the template wasn't present. That is, before the template is examined, the menulist would have one item in it, with the label 'All'. The template generates two results as before, one for each country, and appends a <menuitem> for each one.

One interesting thing about this example is that only one <menupopup> will be created, even though there are two in the code, one outside the template and another one inside the action body. This demonstrates how the template builder generates content, and the specifics of how this is done become important when dealing with recursive content and multiple rules.

What's going on here is that the XUL parser first creates the outer <menulist> element. The static content outside the template will also be created. The result will be a XUL display that looks just like the code above, except that the template and its content is hidden. At this point, the menulist will have a menupopup as a child, and the menupopup will in turn have one menuitem child. Next, the template is examined and two results are generated. Remember that only the content at the uri attribute or below inside the action are copied for each result.

When processing the action body, the builder looks at the first element inside the <action>, in this case, the <menupopup>. This element doesn't have a uri attribute, so the builder knows that this element should only be created once. You might think that the builder would then just create a <menupopup>, but it doesn't. Instead, it looks to see if a <menupopup> already exists in the location where it would be generated. In this case, there is one, the static menupopup defined outside the template. Instead of creating another menupopup, the builder uses the existing one. This is convenient since it makes it easy to combine static results and template generated results into one list.

The builder then switches where generated content would be generated to inside to existing menupopup and moves onto the next part of the action, the <menuitem>. This element has the uri attribute so this content will be duplicated for each result.

The technique of looking for an existing element applies to any type of content. The builder will treat the element as already present as long as some element with the same tag name exists. If you were to change the static content to be some other element besides a <menupopup>, it won't match, so the builder will create a new one when handling the action. This becomes useful when using multiple rules and we'll see some examples of this later.

Here is the example to try. Note that the workaround of loading the datasource beforehand as mentioned for the last example isn't necessary, as the existence of the static content is another effective workaround. So, if you're using menulists with templates and the data isn't displaying properly when the data hasn't loaded, try using a static <menupopup>. It doesn't even need to have any items in it.

The static content may be placed before or after the template, although usually you would place it first. Note that the static content is always placed before the generated content, so the 'All' item will always be the first item in the menu.

Let's look at another example, involving radio buttons. Actually, this example is similar, it just uses a radigroup instead of a menulist to display the list of countries.

<radiogroup datasources="template-guide-photos4.rdf"
            ref="http://www.daml.org/2001/09/countries/country-ont#Country"
            onselect="applyFilter(event.target.value);">
  <radio label="All" selected="true"/>
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <triple subject="?country"
                predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
                object="?start"/>
        <triple subject="?country"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="?countrytitle"/>
      </conditions>
      <action>
        <radio uri="?country" label="?countrytitle" value="?country"/>
      </action>
    </rule>
  </template>
</radiogroup>

This example doesn't have any other content to generate outside the radio element with the uri attribute, so it will just be copied as is. The static 'All' radio button will be left as the first radio item.

Comments ( 4 )

June 30, 2005

8:06 PM How Templates Work XVII - Generating a Filter Menu

Templates may be used to generate any type of content. It is common to use a template to generate the items on a menu or in a listbox or tree. The syntax is the same regardless of what type of content is being created. In the previous example, we hard-coded a menulist with the list of countries, but we could also generate this list from the datasource. The same datasource may used for both the photos list and the menulist. Even though the same datasource is used, it will only be loaded once and both templates will be notified when the data has loaded.

We will need to add some information to the datasource in order to specify the list of countries that are available. There are two possibilities. First, a separate Seq could be added listing the countries. Then, a simple <member> condition can be used to iterate over it. Or, we could use an RDF type to specify the countries. Then, we could just look for all resources that were of the 'Country' type. We'll use this method here since we've already seen examples of generating results from a container.

An RDF type can be assigned to a node by using the predicate 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' set to a resource for that type. In RDF/XML, a syntax shortcut may be used which involves replacing the Description tag with the type. We need to add the type to the two countries in the datasource, as well as ensure that the namespace is declared on the root RDF tag:

<nso:Country about="http://www.daml.org/2001/09/countries/iso#IT"
             dc:title="Italy"/>
<nso:Country about="http://www.daml.org/2001/09/countries/iso#NL"
             dc:title="Netherlands"/>

The type of these two resources, when expanded with the namespace (not shown here), will be 'http://www.daml.org/2001/09/countries/country-ont#Country'. The resulting RDF triples for the first country will be:

http://www.daml.org/2001/09/countries/iso#IT
  -> http://www.w3.org/1999/02/22-rdf-syntax-ns#type
  -> http://www.daml.org/2001/09/countries/country-ont#Country
http://www.daml.org/2001/09/countries/iso#IT
  -> http://purl.org/dc/elements/1.1/title
  -> Italy
The type is just like any other triple in the datasource, so we don't need any special syntax to navigate over it. We just need to use the right predicate to look for all the countries. You might wonder what the ref attribute or starting point should be set to since there is no container for the countries. Actually, we can just use the type as the starting point.

<menulist datasources="template-guide-photos4.rdf"
          ref="http://www.daml.org/2001/09/countries/country-ont#Country">

Remember that the only requirement is that the starting point be a resource, but it doesn't matter what resource is used. The conditions will need to iterate over the arcs pointing into the type resource. Since we want the arcs pointing into the type, we need to determine the source or subject of a triple. Look at the resulting RDF triples above again if this is unclear. The starting node is 'http://www.daml.org/2001/09/countries/country-ont#Country'. We need to iterate over the 'type' predicate to find the individual countries. A second triple is used to get the title for the country.

<conditions>
  <content uri="?start"/>
  <triple subject="?country"
          predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
          object="?start"/>
  <triple subject="?country"
          predicate="http://purl.org/dc/elements/1.1/title"
          object="?countrytitle"/>
</conditions>

The action body will need to generate a <menuitem> for each result. It will also need to include a <menupopup> around all of the items. Since we only want to generate a single menupopup, it should go outside of the element with the uri attribute. Only the contents at the element with the uri attribute and below are copied for each result. The elements above are not.

<action>
  <menupopup>
    <menuitem uri="?country" label="?countrytitle" value="?country"/>
  </menupopup>
</action>

The result will be two generated menuitems, one for each country, inside a menupopup. The value attribute is assigned the ?country variable so that the applyFilter function can use this value easily for filtering. The complete example shows this working. Note that due to a bug with template generation in menulists when the datasource hasn't loaded yet, you may have to load the example twice to get it work. This issue only affects the menulist element. Several workarounds can be used in this simple example. First, you could move the <menupopup> element outside of the template and put the datasources attribute on it instead of the <menulist> element. This will work in this simple example, but has the disadvantage that the builder won't be able to generate the content lazily only when the popup is opened. Another workaround is to just rebuild the template once the data has been loaded.

Next, we will look at how to add the All choice to the menu, which won't be generated from the datasource.

Comments ( 8 )

June 29, 2005

7:28 PM How Templates Work XVI - Additional Navigation and Filtering

The last example showed how to navigate 'upwards' in the RDF graph. By carefully defining the RDF graph and using the right triples we can usually navigate to any part of the RDF data that is necessary in order to get the right results. Usually, however you will want to iterate only over the children of a node using a <member> tag and use a <triple> or <binding> for each predicate of each child that you wish to display. This is the way that the template system was originaly designed to be used and many of the datasources used by Firefox and other Mozilla products work with datasources in this way.

A very uncommon form of navigation you can also do is to navigate upwards using a <member> tag, that is to get all the parents of a node, as in this example:

<conditions>
  <content uri="?start"/>
  <member container="?parent" child="?start"/>
</conditions>

This might be used, for instance, to start at a particular photo and find all of the containers that it is inside.

Sometimes you may wish to change the conditions at a later time. For example, you wish to user to be able to select a value from a list, and the template results should be filtered based on that value. This can be done by modifying the DOM nodes inside the conditions and rebuilding the template. For instance, to apply a filter, you might add a new <triple> element. To remove the filter, the <triple> should be removed again. Let's assume that we've given an id of 'cond' to the <conditions> tag.

function applyFilter(country)
{
  var cond = document.getElementById("cond");
  var triple = document.getElementById("filterTriple");
  if (country) {
    if (!triple) {
      triple = document.createElement("triple");
      triple.id = "filterTriple";
      triple.setAttribute("subject", "?photo");
      triple.setAttribute("predicate", "http://www.daml.org/2001/09/countries/iso-3166-ont#Country");
    }
    triple.setAttribute("object", country);
    cond.appendChild(triple);
  }
  else if (triple) {
    cond.removeChild(triple);
  }
  document.getElementById("photosList").builder.rebuild();
}

The 'country' argument to the applyFilter function holds the value to filter by. If this is set, we add a filter, otherwise we remove it. The code is fairly straightforward. A new <triple> element is created and the object attribute is set to the value to filter by. For example, the resulting triple for the Netherlands might be:

<triple subject="?photo"
        predicate="http://www.daml.org/2001/09/countries/iso-3166-ont#Country"
        object="http://www.daml.org/2001/09/countries/iso#NL"/>

This triple is then appended to the conditions. The triple is given an id so that it can be found later if a different filter is applied. Naturally, we only want to apply one filter at a time, so we can just reuse the same triple for each filter. When removing the filter, we only need to remove the triple from the conditions. This example only adds one triple, but you could add others, or add <member> elements. Whether a triple or member is added or removed, the template must be rebuilt by calling the rebuild method. This method will remove all of the existing generated content, delete all of the internal information pertaining to the results, and start again as if the template was just being examined for the first time. The template will be parsed again and the data examined for new results.

You can view a complete example of this in action. A menulist is used to allow one to select either a specific country, or select All to show all of the photos. When a choice is made, the applyFilter function as shown above is called and the template content gets rebuilt with the desired filter applied.

In this example, the menulist is hard-coded to contain the items that we know are in the datasource. Next, we'll look at also generating this list using a template.

Comments ( 8 )

June 26, 2005

3:53 PM How Templates Work XV - Source Navigation

Triples may also be used to navigate up the graph as well as downwards, following the graph arrows in the reverse direction. For instance, in the original example, we could start at node D and navigate upwards to A and C. The syntax for the triple is the same except that the known variable should be placed in the triple's object attribute and the unknown variable should be placed in the triple's subject variable.

<conditions>
  <content uri="?start"/>
  <triple subject="?relateditem"
          predicate="http://www.xulplanet.com/rdf/relatedItem"
          object="?start"/>
</conditions>

The triple is evaluated in the same manner except that the value of the object can be filled in with the value of the ?start variable. The RDF graph is examined for an arrow pointing in to the node instead of out. In the photos example, we might wish to categorize each photo into several categories. One possibility is to just add another predicate for each photo with a literal value set to the category. Or, we could use a resource for each category. This will allow us to associate additional properties to each category and more easily navigate the graph in a template. In this case, we will categorize each photo by country where the photo was taken using a country predicate.

<?xml version="1.0"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:dc="http://purl.org/dc/elements/1.1/"
         xmlns:nso="http://www.daml.org/2001/09/countries/iso-3166-ont#">
  <rdf:Seq rdf:about="http://www.xulplanet.com/rdf/myphotos">
    <rdf:li rdf:resource="http://www.xulplanet.com/ndeakin/images/t/palace.jpg"/>
    <rdf:li rdf:resource="http://www.xulplanet.com/ndeakin/images/t/canal.jpg"/>
    <rdf:li rdf:resource="http://www.xulplanet.com/ndeakin/images/t/obelisk.jpg"/>
  </rdf:Seq>
  <rdf:Description rdf:about="http://www.xulplanet.com/ndeakin/images/t/palace.jpg"
                   dc:title="Palace from Above">
    <nso:Country resource="http://www.daml.org/2001/09/countries/iso#IT"/>
  </rdf:Description>
  <rdf:Description rdf:about="http://www.xulplanet.com/ndeakin/images/t/canal.jpg"
                   dc:title="Canal">
    <nso:Country resource="http://www.daml.org/2001/09/countries/iso#NL"/>
  </rdf:Description>
  <rdf:Description rdf:about="http://www.xulplanet.com/ndeakin/images/t/obelisk.jpg"
                   dc:title="Obelisk">
    <nso:Country resource="http://www.daml.org/2001/09/countries/iso#IT"/>
  </rdf:Description>
  <rdf:Description about="http://www.daml.org/2001/09/countries/iso#IT"
                      dc:title="Italy"/>
  <rdf:Description about="http://www.daml.org/2001/09/countries/iso#NL"
                      dc:title="Netherlands"/>
</rdf:RDF>

A new Country predicate has been added to each photo pointing to another resource. Note that this predicate uses a new namespace so it is declared on the root RDF element. The titles of the two countries are defined near the end of the RDF data. Now that we have countries for each photo, instead of starting at the node 'http://www.xulplanet.com/rdf/myphotos', we could start at a country resource and look for all photos that point to that country. The ref attribute will need to changed to point to the country resource rather that the Seq container of photos.

<hbox datasources="template-guide-ex2.rdf"
      ref="http://www.daml.org/2001/09/countries/iso#IT">

In this example, three triples will be used.

<conditions>
  <content uri="?start"/>
  <triple subject="?start"
             predicate="http://purl.org/dc/elements/1.1/title"
             object="?countrytitle"/>
  <triple subject="?photo"
             predicate="http://www.daml.org/2001/09/countries/iso-3166-ont#Country"
             object="?start"/>
  <triple subject="?photo"
             predicate="http://purl.org/dc/elements/1.1/title"
             object="?title"/>
</conditions>

The first triple is used to get the title for the country. There is only one potential result at this point, the starting node which is 'http://www.daml.org/2001/09/countries/iso#IT'. After the first triple is examined, the data network will look like the following:

(?start = http://www.daml.org/2001/09/countries/iso#IT,
 ?countrytitle = Italy)

Only one result is present since there is only one title available. The second triple is the more interesting one. It is used to look up nodes that have a Country predicate pointing in. It may be difficult to tell whether the builder will look for arcs pointing out from a node or into a node just by looking at the <triple> element. The template builder doesn't know this either until the triple is evaluated and variable substitution is done. Here, the value of the ?start variable is already known for the potential result, so it will be filled in. However, the value of the ?photo variable is not known. Since the object is known and the subject is not, the builder knows to look for a predicate pointing in to a node. There are two photos in the RDF datasource that point to the node 'http://www.daml.org/2001/09/countries/iso#IT', so the existing result will be copied for these two photos. The resulting data after the second triple will look like this:

(?start = http://www.daml.org/2001/09/countries/iso#IT,
 ?countrytitle = Italy,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/palace.jpg)
(?start = http://www.daml.org/2001/09/countries/iso#IT,
 ?countrytitle = Italy,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/obselisk.jpg)

The third triple just grabs the title from the photo as previous examples did, adding a value for the ?title variable for each photo. We can then display these results using the following action body.

<vbox uri="?photo" class="box-padded">
  <image src="?photo"/>
  <label value="?title"/>
  <label value="Country: ?countrytitle"/>
</vbox>

A label for the country will be displayed as well. Naturally, it will have the same value for every photo. Some padding has been added around each photo using the 'box-padded' class. Here is the example.

Comments ( 11 )