Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Laura Holland 82 posts 103 karma points
    Sep 24, 2011 @ 19:02
    Laura Holland
    0

    Count number of siblings and loop through

    I'm sure this must be simple, but I don't understand how to count the number of sibling nodes, and then to do a basic previous/next navigation loop through these nodes, with the beginning node looping to the last (like a carousel).

    I had it working great with previous/next buttons that disappeared when there was no previous/next node, but the client doesn't want that - they want it to loop through the nodes, so that if there is no previous, it will loop back around.

    I also need to count the number of siblings nodes, so that if there is only one node at that particular level, to not not display the navigation. I would have that this is easy, but I haven't been able to figure it out after several hours.

    I'm having such a hard time with XSLT and XPath. I've been working with it for months, but I don't feel like I'm getting anywhere. Every time I have a new project, it feels like I need to learn everything from scratch. Hopefully it will all click at some point.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Sep 24, 2011 @ 22:14
    Chriztian Steinmeier
    1

    Hi Laura,

    I'll try to help you through this then - XSLT can be hard to learn, depending on where you come from in the code world - it definitely takes a whole 'nother approach (which I hapen to like a lot):

    <xsl:template match="/">
        <xsl:apply-templates select="$currentPage" />
    </xsl:template>
    
    <!-- Template for $currentPage  -->
    <xsl:template match="*[@isDoc]">
        <xsl:variable name="first" select="../*[@isDoc][1]" />
        <xsl:variable name="last" select="../*[@isDoc][last()]" />
        <xsl:variable name="prev" select="preceding-sibling::*[@isDoc][1]" />
        <xsl:variable name="next" select="following-sibling::*[@isDoc][1]" />
    
        <!-- More than this one? -->
        <xsl:if test="preceding-sibling::*[@isDoc] | following-sibling::*[@isDoc]">
            <xsl:text>Previous: </xsl:text>
            <xsl:apply-templates select="$prev | $last[not($prev)]" mode="link" />
    
            <xsl:text> Next: </xsl:text>
            <xsl:apply-templates select="$next | $first[not($next)]" mode="link" />
    
        </xsl:if>
    </xsl:template>
    
    <!-- Template for the link -->
    <xsl:template match="*[@isDoc]" mode="link">
        <a href="umbraco.library:NiceUrl(@id)" title="{@nodeName}">
            <xsl:value-of select="@nodeName" />
        </a>
    </xsl:template>
    

    In root template we just apply templates for $currentPage, which will save us from having to write that in front of every selector.

    In the template that's run for $currentPage, we first select the bits we need - remember, in XSLT almost everything is a "set", even if it's empty. The first and last siblings are easier to select by going up to the parent and then take its first and last children.

    The previous and next siblings are selected using their respective axes - the important part here is the predicate ([1]) that selects only the first node from that set; This is where many attempts to do this fail...

    So far, so good - then we create a set consisting of all the preceding siblings *and* all the following siblings - if that set is empty, there is only the current page and we skip the rest.

    Otherwise, we'll do the actual output. For this, we create another template with a "link" mode, which we'll use in a second.

    To do the "select the last sibling if no previous exist" thing, we use a little trick: Create a set from the $prev node and (if that is empty) the $last node - that way, the set will only ever contain a single node which is the one we want.

    Then we do the same for the "Next" node.

    Bonus tip:

    If your sibling nodes are all of the same Document Type you can greatly improve the readability, by substituting that name for all the "*[@isDoc]" bits in the code, e.g.:

     

    <xsl:variable name="first" select="../Product[1]" />
    <xsl:variable name="last" select="../Product[last()]" />
    <xsl:variable name="prev" select="preceding-sibling::Product[1]" />
    <xsl:variable name="next" select="following-sibling::Product[1]" />
    

    /Chriztian

  • Laura Holland 82 posts 103 karma points
    Sep 24, 2011 @ 23:02
    Laura Holland
    0

    Thank you so very much Chriztian. Your solution works wonderfully, and I really appreciate the time you took to explain how it works!

Please Sign in or register to post replies

Write your reply to:

Draft