Re: [xsl] xslt style

Subject: Re: [xsl] xslt style
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Fri, 10 Jan 2003 12:18:00 +0000
Hi Dave,

> Jeni wrote:
>> .... personally, I think that the only time you should use named
>> templates is when the context node, position and size has no
>> influence on the result of the template -- in other words when it's
>> a pure function that computes its results based purely on the
>> parameters that are passed to it. Otherwise, my preference is to
>> use moded matching templates.
>
> I'm rather curious about the logic behind this Jeni. I can see the
> advantage of this class of use, in that it could then be used
> anywhere, in any stylesheet. Is that the rationale?
>
> Your option of using moded matching templates I'm less clear on.
> Lets say I have a named template that performs some function,
> then uses the parent of the current node for something; is this
> the use-case for the moded template?

Well, here is an example of what I think is a bad design:

<xsl:template match="person">
  <xsl:call-template name="getName" />
</xsl:template>

<xsl:template name="getName">
  <xsl:value-of select="givenName" />
  <xsl:text> </xsl:text>
  <xsl:value-of select="familyName" />
</xsl:template>

The getName template uses the context node at the point the template
was called in order to resolve the two paths ("firstName" and
"surname").

I think that this is bad design because there is no indication in the
getName template about what kind of node the context node is. If I'm
debugging the getName code I have to look at every call to the
template to work out what the context node is in order to work out
what the expressions within the template should be.

I think it is much better to use:

<xsl:template match="person">
  <xsl:apply-templates select="." mode="name" />
</xsl:template>

<xsl:template match="person" mode="name">
  <xsl:value-of select="givenName" />
  <xsl:text> </xsl:text>
  <xsl:value-of select="familyName" />
</xsl:template>

Looking at the latter template tells me all I need to know about what
the template creates. Nothing from outside the template (such as the
context in which it is called) has any influence on the result. This
makes it easy to debug.

It also makes the code more modular, both because you can copy the
same template into other stylesheets and it will standalone, as you
point out, and because it's easier for other people to import and
adapt. If <person> elements with a location attribute of "Japan"
should display the family and given names the other way round it's
easy to add a separate template to do this:

<xsl:template match="person[@location = 'Japan']" mode="name">
  <xsl:value-of select="familyName" />
  <xsl:text> </xsl:text>
  <xsl:value-of select="givenName" />
</xsl:template>

I can see the arguments that for some templates, for example one that
creates an XPath to a node, the identity of the context node doesn't
really matter, but is just being used as a kind of built-in parameter
to the template. Even in these circumstances, I think that the
modularity argument makes me lean towards a moded template, but if
that's not an issue then I think the template should include a
parameter that defaults to the context node rather than using it
implicitly. In other words, rather than:

<xsl:template name="createPath">
  <xsl:for-each select="ancestor-or-self::node()">
    <xsl:value-of select="name()" />
    ...
    <xsl:if test="position() != last()">/</xsl:if>
  </xsl:for-each>
</xsl:template>

I prefer:

<xsl:template name="createPath">
  <xsl:param name="node" select="." />
  <xsl:for-each select="$node/ancestor-or-self::node()">
    <xsl:value-of select="name()" />
    ...
    <xsl:if test="position() != last()">/</xsl:if>
  </xsl:for-each>
</xsl:template>

I prefer this because it makes the template more flexible in that it
can be passed a particular node to use, which means that rather than
having to change the context node with an xsl:for-each:

  <xsl:for-each select="..">
    <xsl:call-template name="createPath" />
  </xsl:for-each>

you can pass a parameter:

  <xsl:call-template name="createPath">
    <xsl:with-param name="node" select=".." />
  </xsl:call-template>

which I find more approachable.

I also prefer the explicit parameter because when I come back to the
template I can immediately tell that the context node is used in the
template and therefore the results will depend on where the template
is called, something that isn't immediately apparent otherwise.

Does that make sense? Anyone think I'm missing something?

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread