how to use parameter in xsl? - xslt

I have a simple xsl file:
<xsl:for-each select="part">
<div class="part">
<h2>Part Name:<xsl:value-of select="#partName" /></h2>
<xsl:for-each select="input">
<p>
<input>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
<xsl:attribute name="name"><xsl:value-of select="#inputName" /></xsl:attribute>
</input>
</p>
</xsl:for-each>
</div>
</xsl:for-each>
I want to save partname and use it latter. I tried as the following:
<xsl:for-each select="part">
<div class="part">
<h2>Part Name:<xsl:value-of select="#partName" /></h2>
**<xsl:param name="part_name"><xsl:value-of select="#partName" /></xsl:param>**
<xsl:for-each select="input">
<p>
<input>
<xsl:attribute name="value">
<xsl:value-of select="." />
</xsl:attribute>
<xsl:attribute name="name">**<xsl:value-of select="$part_name" />**-<xsl:value-of select="#inputName" /></xsl:attribute>
</input>
</p>
</xsl:for-each>
</div>
</xsl:for-each>
but it doesn't work. I don't know what should do.

You are probably getting the error Keyword xsl:param may not be used here.. This is because xsl:param should only really be found under the root xsl:stylesheet element, and are used for passing values from the external application.
What you need is xsl:variable in this case
<xsl:variable name="part_name">
<xsl:value-of select="#partName"/>
</xsl:variable>
Or simpler still....
<xsl:variable name="part_name" select="#partName"/>
In fact, you could do away with the variable altogether. Instead of doing this
<xsl:value-of select="$part_name"/>
You could just do this, to get the value from the parent element
<xsl:value-of select="../#part_name"/>
Do bear in mind, if you do use a variable, it will only be local to the scope of the xsl:for-each in this case.

Related

Create a attribute dynamically

I'm writing an xsl script in which I need to create the attribute dynamically.
Here is my sample code.
<Table BORDERLINESTYLE="solid" BOTTOMMARGIN="12" NOTEGROUPSPACING="single" CELLPADDING="0" CELLSPACING="0" WIDTH="504"></Table>
Here BORDERLINESTYLE may or may not be available to all Table tags. and I want the output to be something like this.
<table style="border-style:solid; border-margin:12px; width:504px"></table>
Here are 2 things that I've tried.
1. Creating attributes
<table>
<xsl:if test="./#BORDERLINESTYLE">
<xsl:attribute name="border" select="'1px solid'"/>
</xsl:if>
<xsl:if test="./#WIDTH">
<xsl:attribute name="width" select="concat(./#WIDTH, 'px')"/>
</xsl:if>
<xsl:if test="./#BOTTOMMARGIN">
<xsl:attribute name="border" select="'1px solid'"/>
</xsl:if>
<xsl:apply-templates/>
</table>
the output that I get is as below.
<table border="1px solid" width="504px">
2. Inline adding
<table style="width:{current()/#WIDTH,'px'}; border:{./#BORDERLINESTYLE}; margin-bottom: {./#BOTTOMMARGIN, 'px'}; margin-top:{./#TOPMARGIN, 'px'}; "></table>
and the output is as below with some blank values like margin-top
<table style="width:504 px; border:solid; margin-bottom: 12 px; margin-top:px; ">
How can I add styles to style based on the attributes provided in the XML?
I'm using XSLT2.0.
I would use something like this:
<xsl:if test="#BORDERLINESTYLE|#WIDTH|#BOTTOMMARGIN|#TOPMARGIN">
<xsl:attribute name="style">
<xsl:if test="#BORDERLINESTYLE">
<xsl:text>border:1px solid;</xsl:text>
</xsl:if>
<xsl:if test="#WIDTH">
<xsl:value-of select="concat('width:',#WIDTH, 'px;')"/>
</xsl:if>
<xsl:if test="#BOTTOMMARGIN">
<xsl:value-of select="concat('margin-bottom:',#BOTTOMMARGIN, 'px;')"/>
</xsl:if>
<xsl:if test="#TOPMARGIN">
<xsl:value-of select="concat('margin-top:',#TOPMARGIN, 'px;')"/>
</xsl:if>
</xsl:attribute>
</xsl:if>
And depending on the business rules for mapping those source-attributes to style attributes change the code accordingly.
Btw.: ./# is the same as #
Use if else Function
For Example
margin-top: {if(//#TOPMARGIN!=0) then {./#TOPMARGIN, 'px'} else '12px'}
I need to create the attribute dynamically.
Actually, you want to populate the attribute dynamically - which could be done by applying a template to each input attribute you want to use:
<xsl:template match="Table">
<table>
<xsl:attribute name="style">
<xsl:apply-templates select="#BORDERLINESTYLE | #BOTTOMMARGIN | #WIDTH"/>
</xsl:attribute>
</table>
</xsl:template>
<xsl:template match="#BORDERLINESTYLE">
<xsl:text>border-style:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>; </xsl:text>
</xsl:template>
<xsl:template match="#BOTTOMMARGIN">
<xsl:text>border-margin:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>px; </xsl:text>
</xsl:template>
<xsl:template match="#WIDTH">
<xsl:text>width:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>; </xsl:text>
</xsl:template>

XSL:Choose query

I've the code as shown below which gives Stylesheet Compilation error.
<xsl:template match="form">
<xsl:copy>
<xsl:for-each select="#*">
<xsl:variable name="param" select="name(.)" />
<xsl:choose>
<xsl:when test="$param = 'name'">
<xsl:attribute name="name"><xsl:value-of
select="#name" /></xsl:attribute>
</xsl:when>
<xsl:when test="$param = 'action'">
<xsl:attribute name="action"><xsl:value-of
select="java:com.hp.cpp.proxy.util.URLUtils.rewriteAction($response, $baseurl, #action, $scope)" /></xsl:attribute>
</xsl:when>
<xsl:when test="$param = 'method'">
<xsl:attribute name="method">POST</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="$param"><xsl:value-of
select="." /></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<input type="hidden" name="httpmethod">
<xsl:attribute name="value"> <xsl:value-of
select="#method" /></xsl:attribute>
</input>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
I'm trying to re-write FORM tag of HTML with quite complex requirement. Hope you'll be able to identify by the code-snap. I'm trying to re-write only few of the attributes of the tag and trying to retain the rest. Is it the right way? Any other way to do it? Any suggestion.
Thanks in advance.
-Rikin
Just a guess. Try to replace the last apply
<xsl:apply-templates select="node()|#*" />
by this
<xsl:apply-templates select="node()" />
About the compilation error, you don't provide enough information to help; Abel's conjecture that the compilation error has to do with your call to an extension function is plausible.
You also ask Is this the right way? to achieve your goal. Maybe. Your first problem here is the logic error that jvverde has already pointed to. The call to apply templates should not select attributes; you have already dealt with all the attributes. So it's unnecessary to process them again. It's also a bad idea: if you you try to handle the form element's attributes again, you'll get a run-time error because you've already written content to the element (namely that input element).
I think some XSLT programmers would write something that looks more like this:
<xsl:template match="form">
<xsl:copy>
<!--* don't use a for-each to handle the
* attributes; use templates. *-->
<xsl:apply-templates select="#*"/>
<!--* you don't need an xsl:attribute constructor
* if you want to use an expression within a
* literal result element; just braces in the
* attribute-value template.
*-->
<input type="hidden"
name="httpmethod"
value="{#method}" />
<!--* change your apply-templates call to
* select children, but not attributes.
*-->
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
<!--* now the attributes ... *-->
<xsl:template match="form/#action">
<xsl:attribute name="action">
<xsl:value-of select="java:com.hp.cpp.proxy.util.URLUtils.rewriteAction(
$response, $baseurl, #action, $scope)" />
</xsl:attribute>
</xsl:template>
<xsl:template match="form/#method">
<xsl:attribute name="method">
<xsl:value-of select="'POST'"/>
</xsl:attribute>
</xsl:template>

XSL counter on specific condition for SharePoint 2010 CQWP

This is the first time I have ever posted a question so apologese in advance if I jibber here.
I am trying to put together a CQWP with jQuery tabs slider functionality. The HTML I want to output should be in the form of 2 UL's. The first with li anchor tags with #associated-ul-id
The second ul's should have ids that associate with the list items in the first. Eg
<div id="tabs" class="news">
<div class="news-pagination">
« Prev
<ul id="carouseltext" class="horizontal-text order">
<li>System</li>
<li>School</li>
</ul>
» Next
<div class="clear"> </div>
</div>
<ul id="tabs-1" class="feed order">
<li>title 1</li>
<li>title 2</li>
</ul>
<ul id="tabs-2" class="feed order">
<li>title 3</li>
</ul>
</div>
The original XML starts off in the form
My XSL goes through the XML twice to fill the 2 ul's. The first time it just adds a new list item when the __begincolumn and __begingroup variables are true. I striped down the functionality here to just output the header. Here's a stripped down version of the XSL
<xsl:template match="/">
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row" />
<xsl:variable name="RowCount" select="count($Rows)" />
<xsl:variable name="FirstRow" select="1" />
<xsl:param name="ColNumber" select="1" />
<xsl:for-each select="$Rows" >
<xsl:variable name="CurPosition" select="position()" />
<xsl:variable name="BeginNewsItemsList1" select="string('<ul id="tabs-')" />
<xsl:variable name="BeginNewsItemsList2" select="string('"class="feed order">')" />
<xsl:variable name="BeginNewsItemsList" select="concat($BeginNewsItemsList1, $ColNumber, $BeginNewsItemsList2)" />
<xsl:if test="($CurPosition >= $FirstRow and $CurPosition <= $LastRow)">
<xsl:variable name="StartNewGroup" select="#__begingroup = 'True'" />
<xsl:variable name="StartNewColumn" select="#__begincolumn = 'True'" />
<xsl:when test="$StartNewGroup and $StartNewColumn">
<xsl:choose>
<xsl:when test="$CurPosition = $FirstRow">
<xsl:value-of disable-output-escaping="yes" select="$BeginNewsItemsList" />
</xsl:when>
<xsl:otherwise>
<!-- other instructions -->
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$StartNewGroup">
<xsl:call-template name="OuterTemplate.CallFooterTemplate"/>
<xsl:value-of disable-output-escaping="yes" select="concat($EndColumn, $BeginNewsItemsList)" />
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="OuterTemplate.Count">
<xsl:param name="ColNumber" />
<xsl:value-of select="$ColNumber + 1" />
</xsl:template>
For the second for-each loop I'm having trouble setting up a counter so that I can add the number to the end of the id for each new list id="tabs-1", id="tabs-2", etc.
In theory I think I should set a parameter outside my for-each loop and then in the loop call a template that gets the parameter value and increments it. That would mean it would increment only when the template is called.
I can't use position() for this as it doesn't correspond to the values I want. I've tried to follow a couple a few blogs about recursive programming with xsl, but I can't seem to find anything that works. I'm sure I'm just writing the XSL wrong, but I'm having a bit of a brain dump now.
If anybody could point me in the right direction that would be awesome. Thanks very much.
You can't change variable's values after declaration. You can use them in expressions and/or pass as parameters. Thus, you can't use outside variable as counter explicitly. One available trick is recursive cycle like:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="root">
<HTML>
<BODY>
<xsl:call-template name="for">
<xsl:with-param name="i" select="1"/>
<xsl:with-param name="n" select="5"/>
</xsl:call-template>
</BODY>
</HTML>
</xsl:template>
<xsl:template name="for">
<xsl:param name="i"/>
<xsl:param name="n"/>
<xsl:value-of select="$i"/>
<xsl:if test="$i < $n">
<xsl:text>, </xsl:text>
<xsl:call-template name="for">
<xsl:with-param name="i" select="$i+1"/>
<xsl:with-param name="n" select="$n"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
result:
1, 2, 3, 4, 5

Looping over hard-coded values in specified in XSL stylesheet, not in XML

I'm wondering if there's a way to loop over a values specified in the XSL, rather than coming from the XML.
Let's say I have 3 possible checkboxes, with a "current" value that comes from the XML. I'd have an XML doc like
<rootNode>
<val>bar</val>
</rootNode>
and XSL code like
<input id="foo" type="checkbox" name="myvar" value="foo">
<xsl:if test="val='foo'">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input> <label for="foo">foo</label>
<input id="bar" type="checkbox" name="myvar" value="bar">
<xsl:if test="val='bar'">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input> <label for="bar">bar</label>
<input id="baz" type="checkbox" name="myvar" value="baz">
<xsl:if test="val='baz'">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input> <label for="baz">baz</label>
This works, but the XSL is very verbose. I'd LIKE to be able to do something like this:
<!-- this syntax doesn't work, is there something similar that does? -->
<xsl:variable name="boxNames" select="'foo','bar','baz'"/>
<xsl:for-each select="name in $boxNames">
<input id="{$name}" type="checkbox" name="myvar" value="{$name}">
<xsl:if test="val=$name">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input> <label for="{$name}"><xsl:value-of select="$name"/></label>
</xsl:for-each>
I can KIND OF get this by putting the code in a template and using multiple <call-template> <with-param> calls, but that's doesn't save much space over the original.
Is there any concise way to do this with XSL? I definitely can't put all the checkbox names in the XML output, it's a large list and unnecessarily bloats the XML.
Yes, you can get the source (!) of the XSL by calling the document('') function, which you can then use as node datasource.
<xsl:template name="boxNames"> <!-- not used as template -->
<name>foo</name>
<name>bar</name>
<name>baz</name>
</xsl:template>
[...]
<xsl:variable name="boxNames" select="document('')/xsl:stylesheet/xsl:template[#name='boxNames']/name" />
Try this, which is very close to the code you suggested:
<xsl:variable name="boxNames" select="'foo','bar','baz'"/>
<xsl:variable name="val" select="val"/>
<xsl:for-each select="$boxNames">
<input id="{.}" type="checkbox" name="myvar" value="{.}">
<xsl:if test="$val=.">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input> <label for="{.}"><xsl:value-of select="."/></label>
</xsl:for-each>
It needs an XSLT 2.0 processor though.
Template matches are effectively for-each loops.
<xsl:template match="/">
<xsl:apply-templates match="namespace:ItemName"/>
</xsl:template>
<xsl:template match="namespace:ItemName">
<input>
<xsl:attribute name="id"><xsl:value-of select="."></xsl:attribute>
<xsl:attribute name="type">checkbox</xsl:attribute>
<xsl:attribute name="name">myvar</xsl:attribute>
<xsl:attribute name="value"><xsl:value-of select="."></xsl:attribute>
<xsl:value-of select=".">
</input>
<label>
<xsl:attribute name="for"><xsl:value-of select="." /></xsl:attribute>
<xsl:value-of select="."/>
</label>
</xsl:template>
That should cut it down.

with-param not working in apply-templates

I am currently trying to generate the creation SQL for my tables based off of a Visio diagram. I am doing this using the approach found here.
http://www.dougboude.com/blog/1/2008/11/SQL-Forward-Engineering-with-Visio-2003-Professional.cfm
I am attempting to modify the xslt file found there to better model the syntax that we use in our office. Unfortunately, I cannot get the part that involves passing the table name into the template for the table columns to work. The template gets called, but it seems to ignore my parameters.
<xsl:template match="Entity" mode="table">
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name = '<xsl:value-of select="#PhysicalName"/>')
<br />
CREATE TABLE dbo.[<xsl:value-of select="#PhysicalName"/>]
(
<br />
<xsl:for-each select="EntityAttributes/EntityAttribute">
<span style="padding-left: 20px;">
<xsl:apply-templates select="../../../../Attributes/Attribute[#AttributeID = current()/#EntityAttributeID]" mode="table">
<xsl:with-param name="EntityName" select="#PhysicalName" />
</xsl:apply-templates>
</span>
<xsl:if test="count(../../EntityAttributes/EntityAttribute) != position()">,</xsl:if>
<br />
</xsl:for-each>
)
<br />
GO
<p />
<xsl:apply-templates select="EntityAnnotations/EntityAnnotation[#AnnotationType='Primary Key']" mode="pk"/>
<xsl:apply-templates select="EntityAnnotations/EntityAnnotation[#AnnotationType='Alternate Key']" mode="ak"/>
<xsl:apply-templates select="EntityAnnotations/EntityAnnotation[#AnnotationType='Index']" mode="idx"/>
</xsl:template>
<!-- Create column for each EntityAttribute -->
<xsl:template match="Attribute" mode="table">
<xsl:param name="EntityName"></xsl:param>
<xsl:variable name="nullability">
<xsl:choose>
<xsl:when test='#AllowNulls = "false"'>NOT NULL CONSTRAINT DF_<xsl:value-of select="$EntityName" />_<xsl:value-of select="#PhysicalName"/>
</xsl:when>
<xsl:otherwise> NULL</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="incremental">
<xsl:choose>
<xsl:when test='#PhysicalDatatype = "int identity"'> INT IDENTITY(1,1)</xsl:when>
<xsl:otherwise><xsl:value-of select="#PhysicalDatatype"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
[<xsl:value-of select="#PhysicalName"/>] <span style="text-transform:uppercase;"> <xsl:value-of select="$incremental"/></span> <xsl:value-of select="$nullability"/>
</xsl:template>
The parameter is not ignored, but I guess it is empty. You call:
<xsl:with-param name="EntityName" select="#PhysicalName" />
where #PhysicalName must be an attribute of EntityAttributes/EntityAttribute element from the for-each. The fact that you use #PhysicalName earlier in
CREATE TABLE dbo.[<xsl:value-of select="#PhysicalName"/>]
makes me think in reality it is an attribute of the Entity element the template matches. You need to store its value in a variable first (before the for-each):
<xsl:variable name="PhysicalName" select="#PhysicalName" />
and then use it like this:
<xsl:with-param name="EntityName" select="$PhysicalName" />
<!-- -------------------------------------^ -->
The for-each resets the context node with every iteration, I guess this is where it goes wrong for you.