Named templates and parameters¶
So far every template has fired by matching a node. A template can instead
be given a name and invoked deliberately, like a subroutine, with
xsl:call-template. This is how you package reusable logic — and, because XSLT
1.0 has no mutable loop counters, how you build recursion.
A template you call by name¶
Add a name attribute and there is no match pattern to satisfy — the template
does nothing until something calls it explicitly.
xsl:call-templateinvokes the rule whosenamematches. Unlikeapply-templates, it names exactly one template — no pattern matching, no node selection.- A
named template with nomatch. It will never fire on its own; only acall-templatereaches it.
Passing arguments: param and with-param¶
A named template becomes useful when it takes arguments. Declare them inside the
template with xsl:param; supply them at the call site with xsl:with-param.
with-parambinds an argument by name. The quoted'Total'is an XPath string literal; without the inner quotesselect="Total"would mean "the child element namedTotal".- The second call omits
value, so its default applies. - A required-looking param with no default behaves as the empty string if the caller leaves it out.
- A param with a default via
select. If the caller passesvalue, that wins; otherwise it is0.
Default by content, not just select
A default can also be written as element content, which is handy for markup:
Use select for an XPath expression (a number, a node-set, a string
literal); use content when the default is literal text or markup.
Names must line up
xsl:with-param name="x" only reaches an xsl:param name="x" in the called
template. A with-param with no matching param is silently ignored — a
classic source of "my argument vanished".
The crucial difference from apply-templates¶
This trips up almost everyone, so state it plainly:
call-template does not change the current node
xsl:call-template runs the named template in the caller's context. The
current node, position(), and last() are all exactly what they were at
the call site. xsl:apply-templates, by contrast, moves to each selected
node — inside the matched template the current node is that node.
Watch the same <cd> data treated both ways:
apply-templatesselects each<cd>and moves to it. Inside thecdtemplate the current node is that<cd>.- So
select="title"resolves against the current<cd>— its own title. call-templatedoes not move. The current node is still this<cd>.- Therefore
select="title"insidehereresolves against that same<cd>— it inherited the context, it did not receive a node.
Run against the catalog, every here call sees
the very node that called it:
Need the call to act on a different node?
call-template has no select, so the only way to hand it a node is through
a parameter: <xsl:with-param name="cd" select="..."/>, then operate on
$cd inside. The current node still does not move — you just have a node-set
in a variable.
Recursion: the XSLT 1.0 loop¶
XSLT 1.0 has no loop with a mutable counter. xsl:for-each iterates over an
existing node-set, but you cannot say "do this N times" or "keep going until a
condition flips" by mutating a variable — variables are immutable. The idiom for
all such problems is a named template that calls itself with a changed
parameter.
Here is "repeat a string N times":
- The base case. When
countreaches0thexsl:ifbody is skipped and the recursion stops. Every recursive template needs one, or it never terminates. - Emit one copy of the string.
- The recursive step: call ourselves with
countdecremented. There is no assignment — the smaller value is passed as a fresh parameter binding.
The same shape walks a delimited list — peel off the head, recurse on the tail — which is the standard way to tokenise a string in XSLT 1.0:
- While a comma remains, there is more than one item: take the part before it as the head, the part after it as the tail.
- Carry the running total in an accumulator parameter — the immutable-variable way to keep state across the recursion.
- Base case: no comma left, so
$listis the final item; add it and emit the total. Calling withlist="3,4,5"yields12.
XSLT 2.0 has real alternatives
If your toolchain is XSLT 2.0/3.0 you also have xsl:for-each over computed
sequences, the tokenize() function, and xsl:iterate — recursion is no
longer the only option. In 1.0 it is, so it is worth getting comfortable
with the head/tail pattern above.
name and match can coexist¶
A template may carry both a name and a match. It then works two ways: it
fires by matching when apply-templates reaches a matching node, and it can also
be invoked directly by name.
| both.xsl | |
|---|---|
- Reached by
<xsl:apply-templates select="cd"/>→ current node is the matched<cd>, and the template runs against it. - Invoked by
<xsl:call-template name="cd-row"/>→ current node is whatever the caller had; if that is not a<cd>,select="title"simply finds nothing.
When to reach for each
Use match templates for walking the document (the push model). Use
named templates for reusable, parameterised logic and recursion —
work that is not naturally "one template per node type".
Next¶
Both xsl:param and xsl:with-param introduce bindings you read with $name.
Variables covers xsl:variable — the third member of that family,
and why "variable" in XSLT does not mean what it does elsewhere.