The Umbraco XSLT for loop

So one of the things I enjoy about teaching the Umbraco Level 2 course is that from time to time I get to go off piste. Someone will ask a question that isn't part of the materials and I have to do a bit of creative thinking to solve what they want to achieve.

Recently I met a chap on my course who didn't like XSLT. I love XSLT, but I wasn't convincing this guy. There are no for loops you see, you can't do something n times. Words like recursion and functional were met with looks of resentment. So I gave up - I gave him an XSLT for loop.

After I bit of hacking I had this XSLT extension method - which I'd never use myself - but hey:

using System.Xml;
using System.Xml.XPath;

using umbraco;

namespace Level2Course.XsltExtensions
{
    [XsltExtension]
    public class SyntaxHighlighter
    {
        public static XPathNodeIterator For(int from, int to)
        {
            var doc = new XmlDocument();
            doc.LoadXml("<iterator></iterator>");

            for(var count=from; count<=to; count++)
            {
                var iterationNode = doc.CreateElement("iteration");
                iterationNode.InnerText = count.ToString();
                doc.DocumentElement.AppendChild(iterationNode);
            }

            return doc.CreateNavigator().Select("//iterator/iteration");
        }
    }
}

This was all hacked together on the course so apologies for the irrelevant namespacing etc.

So the above now allows us to do the common task that is paging using "for loops" in XSLT :)

<?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:Level2Course.XsltExtensions.SyntaxHighlighter="urn:Level2Course.XsltExtensions.SyntaxHighlighter"
  exclude-result-prefixes="msxml umbraco.library Level2Course.XsltExtensions.SyntaxHighlighter">

  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:param name="currentPage"/>
  <xsl:param name="itemsPerPage" select="2"/>

  <xsl:template match="/">
    
    <xsl:variable name="pageUrl" select="umbraco.library:NiceUrl($currentPage/@id)"/>
    <xsl:variable name="childNodes" select="$currentPage/* [@isDoc and string(umbracoNaviHide) != '1']"/>
    
    <xsl:variable name="pageNumber">
      <xsl:choose>
        <xsl:when test="umbraco.library:RequestQueryString('page') <= 1 or string(umbraco.library:RequestQueryString('page')) = '' or string(umbraco.library:RequestQueryString('page')) = 'NaN'">1</xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="umbraco.library:RequestQueryString('page')"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:variable name="numberOfNodes" select="count($childNodes)"/>
    <xsl:variable name="numberOfPages" select="ceiling($numberOfNodes div $itemsPerPage)"/>

    <xsl:variable name="startIndex" select="(($pageNumber -1) * $itemsPerPage) + 1"/>
    <xsl:variable name="endIndex">
      <xsl:choose>
        <xsl:when test="($startIndex + $itemsPerPage) > $numberOfNodes">
          <xsl:value-of select="$numberOfNodes"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="($startIndex + $itemsPerPage) -1"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <div>
      <em>
        Page <xsl:value-of select="$pageNumber"/> of <xsl:value-of select="$numberOfPages"/>.
      </em>
    </div>

    <div>
      <xsl:for-each select="Level2Course.XsltExtensions.SyntaxHighlighter:For($startIndex,$endIndex)">
        <xsl:variable name="counter" select="number(.)"/>
        <xsl:variable name="node" select="$childNodes[$counter]"/>
        <p>
          <xsl:value-of select="$counter"/>. 
          <a href="{umbraco.library:NiceUrl($node/@id)">
            <xsl:value-of select="$node/@nodeName"/>
          </a>
        </p>
      </xsl:for-each>
    </div>

    <div>
      <xsl:if test="$pageNumber > 1">
        <a href="{pageUrl}?page={$pageNumber - 1}">previous</a>
      </xsl:if>

      <xsl:for-each select="Level2Course.XsltExtensions.SyntaxHighlighter:For(1, $numberOfPages)">
        <a href="{pageUrl}?page={.}">
          <xsl:value-of select="."/>
        </a>
      </xsl:for-each>

      <xsl:if test="$pageNumber < $numberOfPages">
        <a href="{$pageUrl}?page={$pageNumber + 1}">next</a>
      </xsl:if>
    </div>
  </xsl:template>

</xsl:stylesheet>

Comments

Leave a comment