XSLT - Call a template with parameters - templates

Can you help me to better understand this part of the code:
When 'template1' is called, what parameters are sent and with which values? What I understand is that the parameter 'xValue' is sent to template, but I don't understand the <xsl:param name="xValue" select="0"/>. Are the two conditions after the template is called to determine the value of the parameter to send?
<xsl:call-template name="template1">
<xsl:with-param name="xValue">
<xsl:choose>
<xsl:when test="string-length($var1)=1 ">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:call-template>
<xsl:template name="template1">
<xsl:param name="xValue" select="0"/>
<xsl:param name="yValue" select="0"/>
<xsl:variable name="newValue">
<xsl:variable name="char" select="substring($xValue,1,1)"/>
<xsl:choose>
<xsl:when test="matches(upper-case($char),'[A-F]')">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="not(string-length($xValue) = 1)">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
</xsl:template>

I don't understand the <xsl:param name="xValue" select="0"/>.
This defines "0" as the default value for the xValue parameter. If you call the template with a different value specified explicitly (as you do in your example), the default value is overridden.
Are the two conditions after the template is called to determine the
value of the parameter to send?
Yes. More precisely, there is one choose statement that determines the value to be sent; it has one test and two values to choose from, according to the result of the test.

<xsl:param name="xValue" select="0"/> is defining a parameter named xValue with a default value of 0.
When you use <xsl:with-param name="xValue"> in your xsl:call-template, you're overriding that default value.

Related

xsl:param used without being assigned a value

I am looking at this xslt template:
<xsl:template match="row">
<xsl:param name="spans"/>
<xsl:param name="browserows"/>
<xsl:choose>
<xsl:when test="contains($spans, '0')">
<xsl:call-template name="normal-row">
<xsl:with-param name="spans" select="$spans"/>
<xsl:with-param name="browserows" select="$browserows"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
I am trying to understand where the value of $spans is coming from at the test statement on line 6, but it looks like the value was never assigned.
I cannot find spans as a global param anywhere.
Am I missing something?
It would come from the calling code that executed the <xsl:apply-templates> that matched against that row - because the <xsl:template> declares that parameter in <xsl:param name="spans"/>. If you didn't specify a value via <xsl:with-param>, then it is presumably nil.

How do we change the value of a variable based on another variable?

I have two variables named editable and display.
If the value of editable is true, I want to set the value of display to 'block'.
If the value of editable is false, I want to set the value of display to 'none'.
This is what I have currently:
<xsl:param name="editable" select="true()"/>
<xsl:choose>
<xsl:when test="$editable">
<xsl:variable name="display" select="'block'"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="display" select="'none'"/>
</xsl:otherwise>
</xsl:choose>
The code above doesn't set the value of display to none.
How do we change the value of a variable based on another variable?
I did not test this, but for me it looks like a problem of scope which might be solved this way:
<xsl:param name="editable" select="true()"/>
<xsl:variable name="display">
<xsl:choose>
<xsl:when test="$editable">
<xsl:value-of select="'block'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'none'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>

Test param value type in XSL

I am getting this from LibXSLT:
XSLTProcessor::transformToXml(): Invalid type
XSLTProcessor::transformToXml(): xmlXPathCompiledEval: 1 objects left on the stack.
I am passing a param which can either have a string value or a nodeset. I am trying to test whether it contains a certain substring and in that case assign that value to another parameter.
The calling template:
<xsl:call-template name="img">
<xsl:with-param name="upload" select="'url.com/image.jpg'"/>
<xsl:with-param name="w" select="200"/>
<xsl:with-param name="h" select="200"/>
</xsl:call-template>
The called template:
<xsl:template name="img" match="*" mode="w">
<xsl:param name="upload" select="."/>
<xsl:param name="JITexternal">
<xsl:choose>
<xsl:when test="
not($upload/meta) and (contains($upload, '.jpg')
or
contains($upload, '.png'))
">
<xsl:value-of select="$upload"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</xsl:param>
</xsl:template>
While I am not sure what is tripping LibXSLT, I think is that fact that when I run those tests and the value is a string it throws the type error above.
But most importantly, is there a good way to test the type of a param's value?
UPDATE: the full XSL script on GitHub
While not exactly type checking, I found that converting the result tree fragment to a string() before running my test did prevent LibXSLT to bomb out:
<xsl:param name="JITexternal">
<xsl:choose>
<xsl:when test="starts-with(string($upload), 'http://')">
<xsl:value-of select="$upload"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</xsl:param>

Pass string into template parameter which expects node?

So,
I have an XSLT template which expects a node set as a parameter and uses this as display text. However, sometimes this node is empty in the XML and I want to pass default display text instead of the display text not showing up instead:
Works:
<xsl:call-template name="myTemplate">
<xsl:with-param name="parm1" select="//element">
</xsl:call-template>
Doesn't work:
<xsl:variable name="dispText">
<xsl:choose>
<xsl:when test="string-length(//element) = 0">
<xsl:value-of select="'Default Text'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//element" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="myTemplate">
<xsl:with-param name="parm1" select="$dispText">
</xsl:call-template>
Any ideas as to how I could accomplish this? I've tried all sorts of things with no luck :(
It seems like all I need to do is create a new node with the display text I want, but I don't know if that is even possible?
Thanks
Implement the default handling in the template, because that's where it belongs. The calling side should be consistent and not have side-effects on the template behavior (i.e. you should not be able to "forget" passing in the default value).
<xsl:template name="myTemplate">
<xsl:param name="parm1" /><!-- node set expected! -->
<!-- actual value or default -->
<xsl:variable name="value1">
<xsl:choose>
<xsl:when test="not($parm1 = '')">
<xsl:value-of select="$parm1" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default1" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- work with $value1 from this point on -->
</xsl:template>
I'm guessing //element is a nodeset and using string-length() on it might not be valid. Try converting it to a string() first?

XSLT: Apply templates with conditional parameters?

I'd like to apply a template with different parameters based on the result of a conditional. Something like this:
<xsl:choose>
<xsl:when test="#attribute1">
<xsl:apply-templates select='.' mode='custom_template'>
<xsl:with-param name="attribute_name" tunnel="yes">Attribute no. 1</xsl:with-param>
<xsl:with-param name="attribute_value" tunnel="yes"><xsl:value-of select="#attribute1"/></xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="#attribute2">
<xsl:apply-templates select='.' mode='custom_template'>
<xsl:with-param name="attribute_name" tunnel="yes">Attribute no. 2</xsl:with-param>
<xsl:with-param name="attribute_value" tunnel="yes"><xsl:value-of select="#attribute1"/></xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select='.' mode='custom_template'>
<xsl:with-param name="attribute_name" tunnel="yes">Error</xsl:with-param>
<xsl:with-param name="attribute_value" tunnel="yes">No matching attribute </xsl:with-param>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
First of all, I suspect that this could be resolved in a much, much better way. (I'm entirely new to XSLT, so please suggest improvements and forgive the bloated code.)
Now for the question: how could I've set the parameters based on this conditional, and still used them in an xsl:apply-templates? I've tried to wrap the entire xsl:choose with a xsl:apply-templates start-/end-tag, but that's apparently not legal. Any clues?
An alternate method would be to put the xsl:choose statements within the xsl:param elements
<xsl:apply-templates select="." mode="custom_template">
<xsl:with-param name="attribute_name" tunnel="yes">
<xsl:choose>
<xsl:when test="#attribute1">Attribute no. 1</xsl:when>
<xsl:when test="#attribute2">Attribute no. 2</xsl:when>
<xsl:otherwise>Error</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="attribute_value" tunnel="yes">
<xsl:choose>
<xsl:when test="#attribute1"><xsl:value-of select="#attribute1"/></xsl:when>
<xsl:when test="#attribute2"><xsl:value-of select="#attribute1"/></xsl:when>
<xsl:otherwise>No matching attribute </xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:apply-templates>
Nothing wrong with your method, but you can also append your conditional into xsl:template match attribute. This will lead to just one xsl:apply-templates, but several xsl:template elements
You can get rid of all that logic and the modes by extracting your conditions into predicates. You don't say what the name of the element you're dealing with is, but assuming it's called foo then something like this should suffice:
<xsl:template match="foo[#attribute1]">
<!--
do stuff for the case when attribute1 is present
(and does not evaluate to false)
-->
</xsl:template>
<xsl:template match="foo[#attribute2]">
<!--
do stuff for the case when attribute2 is present
(and does not evaluate to false)
-->
</xsl:template>
<xsl:template match="foo">
<!--
do stuff for the general case
(when neither attribute1 nor attribute 2 are present)
-->
</xsl:template>