Chaotic PatternExclusively Symphony, straight from the source

Unifying different template methods

Seven years in Tibet taught Allen the true act of selflessness. He teaches the template unification technique with grace and humility.

Seven years in Tibet taught Allen the true act of selflessness. He teaches the template unification technique with grace and humility.

During my 7 year training as a civilian monk at the Commercial Celestial Academy: Tibet Branch (CCATB), I learned the indispensible XSLT technique of unifying matching templates with named templates.

Those who are fluent in XSLT know that there are two template methods: matched and named:

Matched templates like <xsl:template match="body/content/p"> are rule-based methods. Similar to Cascading Style Sheets, matched templates apply a set of instructions based on some criteria. In CSS, the rule body #content > p is akin to body/content/p in XSLT. Matched templates have two advantages: they have knowledge of the context node so you can draw some assumptions with XPath, and they are automatically invoked when a critrion is met.

Named templates like <xsl:template name="get-paragraph"> are function call methods. The benefit of such methods is that the template can be explicitly called wherever your code dictates it rather than having to rely on the XML.

The Monktastic Problem

Here is an example of the two methods put to good use.

Problem: Produce a list of successive pages based on the current-page attribute.

<?xml version='1.0' encoding='utf-8'?>
<data>
    <pagination-info total-entries="45" total-pages="9" entries-per-page="5" current-page="1" />
</data>

For example, if the current page is “1”, XSLT will produce “2, 3, 4, 5, 6, 7, 8, 9” as links. If the current page is “6”, XSLT will produce “7, 8, 9”.

Monktastic Level 1

The solution will require recursion. If you are unfamiliar with the concept, I urge you to watch the Understanding Recursion screencast on the Overture wiki.

<xsl:template match="/">
    <xsl:apply-templates select="data/pagination-info"/>
</xsl:template>

<xsl:template match="pagination-info">
    <xsl:call-template name="pages">
        <xsl:with-param name="context-page" select="@current-page + 1"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="pages">
    <xsl:param name="context-page"/>
    <xsl:value-of select="$context-page"/>
    <xsl:if test="$context-page &lt; @total-pages">
        <xsl:text>, </xsl:text>
        <xsl:call-template name="pages">
            <xsl:with-param name="context-page" select="$context-page + 1"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

The result looks like this:

2, 3, 4, 5, 6, 7, 8, 9

This is a fairly straight-foward recursive template. Upon matching the pagination-info element, the matched template calls the “pages” template where it recursively generate the pages based on the $context-page. Some of you might think the matched template is redundant as it’s possible to simply call the pages template directly. The only catch is that you wouldn’t be able to assume the location of the @current-page attribute which is a benefit of using matched templates.

Monktastic Exercise Level 2

So far, I have been touting this unification of template methods but have yet to really explain what it is. Simply put, the XSLT specification states that it is possible to have both a match and a name attribute in a template instruction. Because of this, we can simplify the code to this:

<xsl:template match="pagination-info" name="pages">
    <xsl:param name="context-page" select="@current-page"/>
    <a href="/entries/page/{$context-page}/">
        <xsl:value-of select="$context-page"/>
    </a>
    <xsl:if test="$context-page &lt; @total-pages">
        <xsl:text>, </xsl:text>
        <xsl:call-template name="pages">
            <xsl:with-param name="context-page" select="$context-page + 1"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

This new template has the perks of both matched and named methods - it has inherent knowledge of the context node and also the ability to be recursive.

A road to self discovery and enlightenment

Another really good use of this unification technique is the date utility found in the default theme in Symphony. The date template can be invoked in 2 ways, either when the date element is matched in the XML or when explicitly called. The effect of this is that all dates are automatically formatted, but in the event that there is an ISO date elsewhere that isn’t a date element, the template can still be explicitly called to format the date.

Discussion

This is a public service announcement: Any person looking to be a certified monk, please call the CCATB hotline. Disclaimer: This certification does not certify the certificate holder is a certified monk.

Almost-guaranteed discussion of intrigue

David Reimer 24 October 07Wordfest participantWordfest word awardWordfest Starting Phrase awardWordfest character limit award

If a tree falls in a forest unobserved … it still fell! If a template is both named and matched, although unseen from the front end, it still does an amazing bit of work!

Would this method provide a way of dealing with this problem: list all available pages, but leave the current page only unlinked. Or is some other approach needed to climb that particular XSLT mountain?

Thanks for this enlightenment!

David

Allen 24 October 07

The aforementioned fancy pantsy technique isn’t really required. Simply use a conditional logic such as xsl:choose to determine if a page is the active page. If not, then have it output a link.

If you require some help with this, don’t hesitate to ask it on the Overture Forum

dale 24 October 07Wordfest participantWordfest word awardWordfest Starting Phrase awardWordfest character limit award

If a tree falls in a forest, I’m sure you would hear it, even if you are not near because your mountain of information on XSLT is quite vast.

My question is in regard to XSLT “for” loops (can’t use the ‘r’ word in this post). Can’t you also use apply-templates to do these loops? I suppose if the loop were simple enough, you would just need to use an ancestor call to the previous element. (not sure if Markdown is enabled here for comments).

Allen 25 October 07

Can’t you also use apply-templates to do these loops? I suppose if the loop were simple enough, you would just need to use an ancestor call to the previous element.

It’s definitely possible to use template matching to do recursion. Recursion is at the heart of XSLT and matching nested templates are also recursive in nature. However, it hadn’t occurred to me to use template matching to do calculation-based recursions. Good call on that one, Dale.

Although it’s possible to use matching for recursion, I would still recommend using named templates for semantic reasons. Also, named templates has the benefit of explicit invocation without the reliance of existing XML nodes.

I’ve done some quick tests on the code above, getting it to output 1,000 page numbers. Matched recursion is on average about 10% slower to process than named recursion. I would guess that the extra overhead with matched recursion goes to node traversals required on every iteration.

I've removed the misleading sentence implying matched templates cannot do recursion. Thank you Dale for pointing that out.

Nick Shepherd 30 October 07Wordfest participantWordfest character limit award

If a tree falls in a forest on the side of a Mountain of Symphony Developers, this article would be invaluable to those monkish developers who plan to rebuild their forest. Without all the Zen - esque phrases here – very good article Allen. You’ve pointed out a possibility that I was completely unaware of with XSLT. This will expand my vocabulary for sure!

Emit your deadly intellect rays