What I have?
I am have an ASP.NET project in which, I have an XSLT file with many templates defined. Only one template will be used at a time depending on the user selection to display the content on a web page. It looks something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="TemplateName"></xsl:param>
<xsl:template match="Title_Only">
...template Title_Only body...
</xsl:template>
<xsl:template match="Image_Only">
...template Image_Only body...
</xsl:template>
<xsl:template match="Title_And_Image">
...template Title_And_Image body...
</xsl:template>
</xsl:stylesheet>
What I want?
I want to pass the template name TemplateName
as a parameter and to be able apply the respective template on the data.
Can someone please tell me how can I achieve this?
You cannot use the value of a param or variable in a match pattern in XSLT 1.0. However, you could apply templates conditionally from a single template, like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="TemplateName"/>
<xsl:template match="/">
<xsl:apply templates select="something[some_condition=$TemplateName]"/>
</xsl:template>
</xsl:stylesheet>
...and then just set-up templates to match each type of node individually. Templates will be applied only to those things that match your select
expression, which is based on the param.
Example of conditionally applying templates
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="TemplateName" select="'Title_Only'" />
<xsl:template match="/">
<xsl:apply-templates select="test/val[@name=$TemplateName]" />
</xsl:template>
<xsl:template match="val">
<xsl:value-of select="@name" />
</xsl:template>
</xsl:stylesheet>
Applied to this input:
<test>
<val name="Title_Only" />
<val name="Image_Only" />
<val name="Title_And_Image" />
</test>
Produces:
Title_Only
...based on the value of $TemplateName
. (Note that this example uses a variable, but the idea is the same.)
Separate templates using modes
If you really need an entirely different template in each case, then use modes. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="TemplateName" select="'Title_Only'" />
<xsl:template match="/">
<xsl:choose>
<xsl:when test="$TemplateName='Title_Only'">
<xsl:apply-templates select="test/val" mode="Title_Only" />
</xsl:when>
<xsl:when test="$TemplateName='Image_Only'">
<xsl:apply-templates select="test/val" mode="Image_Only" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="test/val" mode="Title_And_Image" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="val" mode="Title_Only">
<xsl:value-of select="@title" />
</xsl:template>
<xsl:template match="val" mode="Image_Only">
<xsl:value-of select="@img" />
</xsl:template>
<xsl:template match="val" mode="Title_And_Image">
<xsl:value-of select="@title" />/
<xsl:value-of select="@img" />
</xsl:template>
</xsl:stylesheet>
Applied to this source:
<test>
<val title="some title" img="some image"/>
</test>
Produces:
some title
The desired template is used, based on the value of the param.
I want to pass the template name TemplateName as a parameter and to be able apply the respective template on the data.
In both XSLT 1.0 and XSLT 2.0 it is illegal to have a construct like:
<xsl:call-template name="{$vTemplateName}"/>
While XPath 3.0 (XSLT 3.0) introduces function items and HOF (higher-order functions), HOF can be emulated in previous verions of XSLT. For more information, do read the articles on the home page of FXSL.
Here is a simplified example of the idea lying behind FXSL:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
Given this sample XML, we have two templates, one which produces the sum of all num
elements and the other produces their concatenation. We want to pass the desired operation as a parameter.
Here is how to do this (note that nothing in the source XML itself tells us which operation to use):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pOp" select="'sum'"/>
<my:ops>
<op>sum</op>
<op>concat</op>
</my:ops>
<xsl:variable name="vOps" select=
"document('')/*/my:ops/*"/>
<xsl:template match="/">
<xsl:apply-templates select="$vOps[. = $pOp]">
<xsl:with-param name="arg1" select="/*/*"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="op[.='sum']">
<xsl:param name="arg1"/>
<xsl:value-of select="sum($arg1)"/>
</xsl:template>
<xsl:template match="op[.='concat']">
<xsl:param name="arg1"/>
<xsl:for-each select="$arg1">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When applied on the XML document above, the wanted, correct result is produced:
55
when we replace:
<xsl:param name="pOp" select="'sum'"/>
with:
<xsl:param name="pOp" select="'concat'"/>
and apply the new transformation, again the wanted, correct result is produced:
01020304050607080910
Do note:
The main (using) template can and typically will be in a separate XSLT stylesheet file and will import stylesheets with templates that implement operations. The main template doesn't know what are the operations implemented (and doesn't use an <xsl:choose>
with hardcoded names).
In fact, if templates are added or removed from the imported files, there is no need to modify the main (using) template. In this style of programming the <xsl:apply-templates>
instruction often selects for execution templates which were not yet written, when the main template was produced.
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