Often times you will want to be able to group output of various types of nodes and data within your Web site. Some examples are events that are grouped by date (month, week, etc.) or perhaps you have an FAQ that is grouped by categories. Generally, this sort of output is not rocket science, but XSLT makes it a little tricky. I had a need to output a classified ads section in XSLT so I thought I'd share this with the community as it may help others. I could have accomplished this fairly easily using a C# UserControl, but I prefer to keep my output to XSLT wherever possible.
NOTE: The following example was derived from an article by Jeni Tennison and utilizes the Muenchian Method.
We're going to use the key function which will allow us to easliy reference the node(s) that we care about. Later, we'll refernce this key when looping over the nodes and perfoming comparisons on the current node versus the categories contain within this key.
<xsl:key name="nodes-by-category" match="node" use="string(data [@alias='adCategory'])"/>
As is expected, we'll be nesting a couple of for-each statements here. The first one loops over our 'data' structure (basically the root select statement of the nodes that you are looking to process. In my case I am using GetXmlAll and filtering by documentTypeAlias (see complete example below)).
<xsl:for-each select="$data[count(. | key('nodes-by-category', string(data [@alias='adCategory']))[1]) = 1]">
Basically, the above statement says "if the union of the current node set and the key nodeset only returns 1 match, then we're still the same". This works becuase a node cannot be repeated in a nodeset.
The second for-each simply iterates the nodes that match the current 'category' from our key set:
<xsl:for-each select="key('nodes-by-category', string(data [@alias='adCategory']))">
Armed with this you can now represent your data in a grouped fashion utilizing XHTML and CSS as you normally would. Below is a complete example of what the stylesheet looks like when all this is put together.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets ">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:param name="currentPage"/>
<!-- Input the documenttype you want here -->
<xsl:variable name="documentTypeAlias" select="string('ClassifiedAd')"/>
<xsl:variable name="groupByAlias" select="string('adCategory')"/>
<xsl:variable name="data" select="umbraco.library:GetXmlAll()/descendant-or-self::node [@nodeTypeAlias = $documentTypeAlias and string(data [@alias='umbracoNaviHide']) != '1']"/>
<xsl:key name="nodes-by-category" match="node" use="string(data [@alias='adCategory'])"/> <!-- unfortuantely xsl:key does not accept variables so we have to repeat our field here -->
<xsl:template match="/">
<!-- by category -->
<xsl:for-each select="$data[count(. | key('nodes-by-category', string(data [@alias=$groupByAlias]))[1]) = 1]">
<xsl:sort select="string(data [@alias=$groupByAlias])" />
<h2><xsl:value-of select="data [@alias=$groupByAlias]" /></h2>
<div style="margin-bottom: 20px;">
<div class="itm">
<xsl:for-each select="key('nodes-by-category', string(data [@alias=$groupByAlias]))">
<xsl:sort select="@nodeName" />
<div class="title"><xsl:value-of select="@nodeName"/></div>
<div class="description"><xsl:value-of select="data [@alias=$groupByAlias]"/></div>
</xsl:for-each>
</div>
</div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Hopefully this is helpful to others as well. (written by @nwahlberg)