I don't know if it's possible, but I'm wondering how to do it...
Let's say we have the following XSL:
<xsl:template name="foo">
Bla bla bla
</xsl:template>
...
<xsl:template name="bar">
Bla bla bla
</xsl:template>
...
<xsl:template match="/">
<xsl:if test="$templateName='foo'">
<xsl:call-template name="foo"/>
</xsl:if>
<xsl:if test="$templateName='bar'">
<xsl:call-template name="bar"/>
</xsl:if>
</xsl:template>
Is it possible to change the XSL to read something like...
<xsl:template match="/">
<xsl:call-template name="$templateName"/>
</xsl:template>
It's not possible exactly as you describe, but if you want to be able to choose a template at run-time based on some value you set elsewhere, there is a trick to do that. The idea is to have your named template also match a node with a corresponding name in a distinct mode (so that it doesn't mess up your normal transformation), and then match on that. For example:
<xsl:stylesheet ... xmlns:t="urn:templates">
<!-- Any compliant XSLT processor must allow and ignore any elements
not from XSLT namespace that are immediate children of root element -->
<t:templates>
<t:foo/>
<t:bar/>
</t:templates>
<!-- document('') is the executing XSLT stylesheet -->
<xsl:variable name="templates" select="document('')//t:templates" />
<xsl:template name="foo" match="t:foo" mode="call-template">
Bla bla bla
</xsl:template>
<xsl:template name="bar" match="t:foo" mode="call-template">
Bla bla bla
</xsl:template>
<xsl:template match="/">
<xsl:variable name="template-name" select="..." />
<xsl:apply-templates select="$templates/t:*[local-name() = $template-name]"
mode="call-template"/>
</xsl:template>
Note that you can use <xsl:with-param>
in <xsl:apply-templates>
, so you can do everything with this that you could do with a plain <xsl:call-template>
.
Also, the code above is somewhat lengthier than you might need because it tries to avoid using any XSLT extensions. If your processor supports exslt:node-set()
, then you can just generate nodes directly using <xsl:element>
, and use node-set()
to convert the resulting tree fragment to a plain node to match against, without the need for document('')
hack.
For more information, see FXSL - it's a functional programming library for XSLT that is based on this concept.
No, this is not possible not directly possible. The calling convention is:
<xsl:call-template name="QName" />
Where a QName is defined as:
QName ::= PrefixedName | UnprefixedName
PrefixedName ::= Prefix ':' LocalPart
UnprefixedName ::= LocalPart
Prefix ::= NCName
LocalPart ::= NCName
Basically this boils down to "characters only, no expressions". As the other answers highlight, there are in fact ways to do something equivalent, but the straightforward approach/naïve approach will not work.
For anyone's future reference:
Here is a working example based on Pavel Minaev's answer. No original thought on my part. ;-) I switched it to use msxml:node-set as he described (more or less) so that it works in .NET.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
<xsl:variable name="templates">
<templates>
<foo />
<bar />
</templates>
</xsl:variable>
<xsl:template name="foo" match="foo" mode="call-template">
<FooElement />
</xsl:template>
<xsl:template name="bar" match="bar" mode="call-template">
<BarElement />
</xsl:template>
<xsl:template match="/">
<Root>
<xsl:variable name="template-name">bar</xsl:variable> <!-- Change this to foo to get the other template. -->
<xsl:apply-templates select="msxsl:node-set($templates)/*/*[local-name() = $template-name]" mode="call-template" />
</Root>
</xsl:template>
</xsl:stylesheet>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With