Clean xslt loops with Sitecore and .net

The second time I found myself in this position, I felt I couldn’t get away with it again. Not because of anyone else, simply because it felt wrong. There’s a very low chance this is your second read-through (it’s not old code you wrote), so you probably don’t know what I’m talking about. Please bear with me for a moment as I get to the point.

What happened is that I had to build something similar to something else I build (a bit too quickly) a few weeks before, and of course I went back to take a look in the code. What I noticed was that I found it hard to read, and hard to understand what was supposed to happen when all the code had run. I won’t post all the code here, primarily because I don’t have it anymore but I’ll do my best to reconstruct the part I actually wanted to talk about.

  <ul>
    <xsl:for-each select="$Context/item">
      <li>
              
      </li>
      <xsl:variable name="ULStart" select="'&lt;ul&gt;'" />
      <xsl:variable name="ULEnd" select="'&lt;/ul&gt;'" />
      <xsl:variable name="HR" select="'&lt;hr/&gt;'" />

      <xsl:if test="position() mod 3 = 0 and position() != last()">
        <xsl:value-of select="$ULEnd" disable-output-escaping="yes"/>
        <xsl:value-of select="$HR" disable-output-escaping="yes"/>
        <xsl:value-of select="$ULStart" disable-output-escaping="yes"/>
      </xsl:if>
    </xsl:for-each>     
  </ul>

There was more clutter there but the above should suffice for an example. So, while looking at it a second time, I thought I would rather work with those variables in .net instead, and not in xslt:

  <xsl:for-each select="sc:GroupItems($Context/item, 3)">
    <ul>
      <xsl:for-each select="./item">
        <li>
        </li>
      </xsl:for-each>
    </ul>
    <xsl:if test="position() != last()">
      <hr/>
    </xsl:if>
  </xsl:for-each>

And this is GroupItems with its dependencies:

        private static readonly XPathNavigator emptyNavigator = new XmlDocument().CreateNavigator();
        public XPathNodeIterator CreateEmptyIterator()
        {
            return emptyNavigator.Select("*");
        }
        public XPathNodeIterator GetChildIterator(Packet packet)
        {
            XPathNavigator navigator = packet.XmlDocument.CreateNavigator();
            if (navigator == null)
            {
                return CreateEmptyIterator();
            }
            navigator.MoveToRoot();
            navigator.MoveToFirstChild();
            return navigator.SelectChildren(XPathNodeType.Element);
        }

        public XPathNodeIterator GroupItems(XPathNodeIterator iterator, int group)
        {
            if (group > 1)
            {
                Packet packet = new Packet("groups", "");
                Packet items = new Packet("items", "");
                
                int position = 1;
                int maxPosition = iterator.Count;
                
                while (iterator.MoveNext())
                {
                    System.Xml.IHasXmlNode iHasXmlNode = iterator.Current as System.Xml.IHasXmlNode;

                    if (iHasXmlNode != null)
                    {
                        System.Xml.XmlNode node = iHasXmlNode.GetNode();
                        if (node != null)
                        {
                            items.AddElement(node);
                            if (position % group == 0 || maxPosition == position)
                            {
                                packet.AddElement(items.XmlDocument.DocumentElement);
                                items = new Packet("items", "");
                            }
                            position++;
                        }
                    }
                }
                return GetChildIterator(packet);
            }
            return iterator;
        }

I wrote this code pretty much as I remembered it, with help from auto-complete, so here’s that line again,
Untested, non-production code

Leave a Reply