Is there's a way to print the variable in the element as an attribute?
sample xml:
<list>
<name>John Doe</name>
<name>Paul Niel</name>
<name>Luke Dee</name>
</list>
Here's my sample xslt;
<xsl:variable name="isDisabled">
<xsl:if test="name='John Doe'">
<xsl:attribute name="disabled">disabled</xsl:attribute>
</xsl:if>
</xsl:variable>
and I want to print the isDisabled varible like this;
<input id="textName" name="name" type="text" {$isDisabled} />
output;
<input id="textName" name="name" type="text" disabled="disabled" />
You don't need any variable to accomplish this task.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="name[. = 'John Doe']">
<input id="textName" name="name" type="text" disabled="disabled" />
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<list>
<name>John Doe</name>
<name>Paul Niel</name>
<name>Luke Dee</name>
</list>
the wanted, correct result is produced:
<input id="textName" name="name" type="text" disabled="disabled"/>
Explanation:
Proper use of templates and template pattern matching.
Note: If you have a case (not this one) where it is really necessary to use a variable to create an attribute, this can be done in the following way:
<input id="textName" name="name" type="text" disabled="{$isDisabled}"/>
Explanation:
Proper use of AVT (Attribute Value Templates)
Related
I am trying to define some dynamically created elements as cdata sections, but it's not working for some reason:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml"
cdata-section-elements="DESCRIPTION2"
/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/RSS/ITEM/TEST">
<DESCRIPTION2>
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</DESCRIPTION2>
</xsl:template>
</xsl:stylesheet>
Test XML:
<?xml version="1.0" encoding="UTF-8"?>
<RSS>
<ITEM>
<CODE>41,000</CODE>
<TEST>
<NAME><p>HTML code</p></NAME>
</TEST>
</ITEM>
</RSS>
Live test.
Sure I can add manually (<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>), but I would like to know why it's not working If I define it as cdata-section-elements.
CDATA serialization happens for text nodes inside of the nominated elements, if you put in elements there it doesn't happen. Note that, assuming an XSLT 3 processor with XPath 3.1 support, you can use the serialize function to serialize the content you build as html and then output it as a text node:
<xsl:template match="/RSS/ITEM/TEST">
<xsl:variable name="html">
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</xsl:variable>
<DESCRIPTION2>
<xsl:value-of select="serialize($html, map { 'method' : 'html' })"/>
</DESCRIPTION2>
</xsl:template>
http://xsltfiddle.liberty-development.net/948Fn5i/1 then gives the result as a CDATA section
<DESCRIPTION2><![CDATA[<div class="container">
<div class="test">Peter</div>
</div>]]></DESCRIPTION2>
Your content is well-formed XHTML, so it doesn't need to apply CDATA when serializing the content.
If you escaped the markup and constructed a string, it would serialize as CDATA:
<xsl:template match="/RSS/ITEM/TEST">
<DESCRIPTION2>
<div class="container">
<xsl:if test="NAME != ''">
<div class="test">
<xsl:value-of select="NAME"/>
</div>
</xsl:if>
</div>
</DESCRIPTION2>
</xsl:template>
Produces:
<DESCRIPTION2><![CDATA[
<div class="container">
<div class="test">
Peter
</div>
</div>
]]></DESCRIPTION2>
But why would you want to generate a string when you could have well-formed markup? It makes it a pain for everyone downstream.
I'm doing a series of XSL templates, but we did make them run on php-xsl. The problem I have is with the import and XSL inclucion other files, and palicacion of temples. I have the first file
<xsl:import href="forms.xsl"/>
<xsl:template match="/">
<a id="logo"><xsl:value-of select="web/general/title"/></a>
<xsl:call-template name="search" />
</xsl:template>
</xsl:stylesheet>
and I have forms.xsl file
<xsl:template match="search">
<form>
<label>search</label>
<input type="text" name="search" title="Search" />
<input type="botton" name="search"/>
</form>
</xsl:template>
but when I put on the run, I do not show anything. That's what I'm doing wrong?
In the forms.xsl, you need to change
<xsl:template match="search">
to
<xsl:template name="search">
Match will always match the xml expression, whereas name explicitly gives a name to a template.
If this doesn't work, please post the source xml and what you would like to see in the target.
I have this selectbox on a site Im currently building and what I want to do, is sort a list of items displayed on the page by the value that's being selected.
The problem is that I got different kinds of data types, e.g. location and price.
If I want to sort on location, no problem it sorts perfectly on alphabetic order of the location names. But if I sort on the 'Low budget', which is from low to high in price, I have to add additional tags to the sort (data-type="number"). That works fine, but then the location and such aren't working anymore.
How to solve this to make it handle all kinds of different data types?
My XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:param name="url-filter" select="'recommended'" />
<xsl:template name="resort-list" match="data/resorts/entry">
<form name="sort-form" action="{$root}/resorts/" method="get">
<select name="filter" onChange="document.getElementById('sort-form').submit();">
<option value="">Sort By...</option>
<option value="recommended">Recommended</option>
<option value="location">Location</option>
<option value="prices-from">Low budget</option>
<option value="prices-till">High budget</option>
</select>
</form>
<xsl:for-each select="data/resorts/entry">
<!-- this is were I sort the items -->
<xsl:sort select="*[name() = $url-filter]" />
<a href="{$root}/koh-lipe-resorts/resort-view/{resort-name/#handle}">
<h3 class="resort-item-heading grey"><xsl:value-of select="resort-name"/></h3>
<p>
<xsl:call-template name="truncate">
<xsl:with-param name="value" select="resort-description" />
<xsl:with-param name="length" select="150" />
</xsl:call-template>
</p>
</a>
</xsl:for-each>
</xsl:template>
<xsl:include href="../utilities/master.xsl"/>
</xsl:stylesheet>
My XML:
<data>
<events />
<resorts>
<pagination total-entries="11" total-pages="2"
entries-per-page="10" current-page="1" />
<section id="9" handle="resorts">Resorts</section>
<entry id="114">
<price-from handle="1200">1200</price-from>
<price-till handle="1900">1900</price-till>
<recommended>No</recommended>
<lipe-green-aware-resort>No</lipe-green-aware-resort>
<name>Baja Resort</name>
<location>Sunrise Beach</location>
</entry>
</resorts>
</data>
This transformation correctly sorts for each possible parameter value: "recommended" or "price-from":
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="url-filter" select="'price-from'" />
<xsl:variable name="vSortOrder">
<xsl:choose>
<xsl:when test="$url-filter = 'price-from'">
<xsl:text>ascending</xsl:text>
</xsl:when>
<xsl:otherwise>descending</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="vSortType">
<xsl:choose>
<xsl:when test="$url-filter = 'price-from'">
<xsl:text>number</xsl:text>
</xsl:when>
<xsl:otherwise>text</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:template name="resort-list" match="data/resorts">
<form name="sort-form" action="/*/resorts/" method="get">
<select name="filter" onChange="document.getElementById('sort-form').submit();">
<option value="">Sort By...</option>
<option value="recommended">Recommended</option>
<option value="location">Location</option>
<option value="prices-from">Low budget</option>
<option value="prices-till">High budget</option>
</select>
</form>
<xsl:for-each select="entry">
<!-- this is were I sort the items -->
<xsl:sort select="*[name() = $url-filter]"
data-type="{$vSortType}" order="{$vSortOrder}" />
<a href="/*/koh-lipe-resorts/resort-view/{resort-name/#handle}">
<h3 class="resort-item-heading grey"><xsl:value-of select="name"/></h3>
</a>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>
</xsl:stylesheet>
With the this XML document (extending the provided with just one more entry):
<data>
<events />
<resorts>
<pagination total-entries="11" total-pages="2"
entries-per-page="10" current-page="1" />
<section id="9" handle="resorts">Resorts</section>
<entry id="114">
<price-from handle="1200">1400</price-from>
<price-till handle="1900">1900</price-till>
<recommended>No</recommended>
<lipe-green-aware-resort>No</lipe-green-aware-resort>
<name>Baja Resort</name>
<location>Sunrise Beach</location>
</entry>
<entry id="115">
<price-from handle="1500">1500</price-from>
<price-till handle="1700">1700</price-till>
<recommended>Yes</recommended>
<lipe-green-aware-resort>No</lipe-green-aware-resort>
<name>Blah-Blah Resort</name>
<location>Blah-Blah Beach</location>
</entry>
</resorts>
</data>
and with each of the two possible values of the $url-filter parameter, the correct results are produced sorted with the correct sort-key data type and in the correct sort order (ascending or descending).
Good question. Here's a way to do it:
Create a named template for your
inner <a> element, call it "link"
for example.
Create two named templates for your sorted for-each: "sort-by-alpha"
and "sort-by-number".
"sort-by-number" does a for-each, sorts by number, and calls the "link" template.
"sort-by-alpha" does a for-each, sorts (by alpha), and calls the "link" template.
In your outer template, outside of where you currently have for-each on data/resorts/entry, put a choose-when-test-otherwise that decides to call "sort-by-number" when the sort criterion is prices-from or prices-till; otherwise, it calls sort-by-alpha.
Does that make sense? Let me know if this doesn't do it for you.
I have 2 pieces of code in my XSL file.
1)
<xsl:call-template name="Info">
<xsl:with-param name="parententry" select="a:feed/a:entry/#href" />
</xsl:call-template>
2)
<xsl:template name="Info">
<xsl:param name="parentEntry" />
<xsl:variable name="parententryauthorname">
<xsl:value-of select="a:author/a:name" />
</xsl:variable>
<xsl:variable name="info2">
<xsl:value-of select="$parentEntry" />
</xsl:variable>>
<input name="info2" type="hidden" value="{$info2}" />
<input name="parententryauthorname" type="hidden" value="{$parententryauthorname}" />
</xsl:template>
What I want to do is to assign a value to "parententry" at 1st piece of code , and then refer it at the 2nd place. When I am in the "Info" template, I want to further process the value assigned in "parententry" to get the author's name.
Right now, when I tried to print the value of $info2, and $parententryauthorname, they are both empty.
Any suggestions?
This stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
exclude-result-prefixes="atom">
<xsl:template match="/">
<xsl:apply-templates select="*/atom:entry" mode="form"/>
</xsl:template>
<xsl:template match="atom:entry" mode="form">
<input name="info2" type="hidden"
value="{atom:link[#rel='alternate']/#href}" />
<input name="parententryauthorname" type="hidden"
value="{atom:author/atom:name}" />
</xsl:template>
</xsl:stylesheet>
With this page's feed, output:
<input name="info2" type="hidden" value="http://stackoverflow.com/questions/3754954/how-to-refer-a-variable-within-xsl-from-one-function-to-another-function" />
<input name="parententryauthorname" type="hidden" value="Java Doe" />
<input name="info2" type="hidden" value="http://stackoverflow.com/questions/3754954/how-to-refer-a-variable-within-xsl-from-one-function-to-another-function/3755184#3755184" />
<input name="parententryauthorname" type="hidden" value="Alejandro" />
Note: When processing nodes in differents ways, modes are the right tool.
Edit: Update the output with the new feed. Just for fun!
I need to change the order in which my for-each is executed only if some conditions are met.
Here is what my XML looks like :
<OptionList>
<Option name="My First Option" />
<Option name="My Second Option" />
</OptionList>
However, in some case, my XML can be like this :
<OptionList>
<Option />
<Option name="My Second Option" />
</OptionList>
In my XSL, I'm doing a for-each like this :
<xsl:for-each select="//OptionList/Option">
{...}
</xsl:for-each>
I know I can change orders of Option nodes using this line in my for-each :
<xsl:sort select="position()" data-type="number" order="descending" />
The problem is that I want my order to be descending only when my first Option node is empty and doesn't have the name attribute. Otherwise, I want to keep the default ascending order.
Any input on how I can acheive that? So far, everything I tried ended up with "Variable out of scope" or invalid use of xpath functions.
You can use a hack to change the sort order based on a condition:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<result>
<xsl:for-each select="//OptionList/Option">
<xsl:sort data-type="number" order="ascending"
select="position()*(-2*number(not(//OptionList/Option[1]/#name))+1)"/>
<option>
<xsl:value-of select="#name"/>
</option>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>
The hack is that number((true()) returns 1 and number(false()) returns 0. As a consequence, the expression
-2 * number(not(//OptionList/Option[1]/#name)) + 1
evaluates to 1 if the first option element has a name attribute and to -1 otherwise. This is used as a factor to reverse the sort order.
The order of producing the wanted output can be easily specified with <xsl:sort>:
This is natural and easy and involves almost no hacks.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vOrder" select=
"2*boolean(/*/Option[1]/#name)-1"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each select="Option">
<xsl:sort data-type="number" select="$vOrder* position()"/>
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this transformation is performed on the this XML document:
<OptionList>
<Option name="My First Option" />
<Option name="My Second Option" />
<Option name="My Third Option" />
</OptionList>
the wanted, correct result is produced:
<OptionList>
<Option name="My First Option"/>
<Option name="My Second Option"/>
<Option name="My Third Option"/>
</OptionList>
When the same transformation is now performed on this XML document:
<OptionList>
<Option />
<Option name="My Second Option" />
<Option name="My Third Option" />
</OptionList>
the wanted, correct result is produced again:
<OptionList>
<Option name="My Third Option"/>
<Option name="My Second Option"/>
<Option/>
</OptionList>
Explanation: The variable $vOrder is defined in such a way, that it is -1 iff the first Option element has no name attribute and it is +1 if the first Option element has a name attribute. Here we use the fact that false() is converted automatically to 0 and true() to 1.
We also use the fact that when the sign of each number in sequence of increasing positive numbers (the positions) is reversed, the order of the new sequence becomes decreasing.
Check if the first option has the name node or not
<xsl:if test="Option/[position()=1]/#name">sort here</xsl:test>