Copied to clipboard

Flag this post as spam?

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


  • Martin 278 posts 662 karma points
    Apr 25, 2013 @ 12:36
    Martin
    0

    XSLT Group Nodes In 2 or 3

    Hi, 

    Im looking for some help with grouping my nodes.

    I have a multi node tree picker set up and outputting my nodes correctly.

    What I would like is to group / wrap a div, around the first two nodes and then group subsequent nodes in threes.

    Any help would be grateful.

    The xslt I have so far is...

    <xsl:if test="$currentPage/homeFeatureBlock/MultiNodePicker/nodeId">
    <!--VARIBALE FOR BLOCKS OF 2-->
    <xsl:variable name="N2" select="2"/>
    <!--VARIBALE FOR BLOCKS OF 3-->
    <xsl:variable name="N3" select="3"/>
    <xsl:for-each select="$currentPage/homeFeatureBlock/MultiNodePicker/nodeId">
    <xsl:variable name="node" select="umbraco.library:GetXmlNodeById(.)" />
    <xsl:choose>
    <xsl:when test="position() &lt; '3'">
    <!-- OUTPUT DIV OPENING TAG -->
    <xsl:if test="position() mod $N2 = 1">
    <xsl:value-of select="'&lt;div class=&quot; row 2 &quot;&gt;'" disable-output-escaping="yes"/>
    </xsl:if>
    <div class="block">
    <h4><xsl:value-of select="$node/@nodeName" /></h4>
    <p><xsl:value-of select="$node/blockDescription" disable-output-escaping="yes"/></p>
    </div>
    <!-- OUTPUT DIV CLOSING TAG -->
    <xsl:if test="position() mod $N2 = 0">
    <xsl:value-of select="'&lt;/div&gt;'" disable-output-escaping="yes"/>
    </xsl:if>
    </xsl:when>
    <xsl:otherwise>
    <!-- OUTPUT DIV OPENING TAG -->
    <xsl:if test="position() mod $N3 = 1">
    <xsl:value-of select="'&lt;div class=&quot; row 2 &quot;&gt;'" disable-output-escaping="yes"/>
    </xsl:if>
    <div class="block">
    <h4><xsl:value-of select="$node/@nodeName" /></h4>
    <p><xsl:value-of select="$node/blockDescription" disable-output-escaping="yes"/></p>
    </div>
    <!-- OUTPUT DIV CLOSING TAG -->
    <xsl:if test="position() mod $N3 = 0">
    <xsl:value-of select="'&lt;/div&gt;'" disable-output-escaping="yes"/>
    </xsl:if>
    </xsl:otherwise>
    </xsl:choose>
    </xsl:for-each>
    </xsl:if>

     

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Apr 25, 2013 @ 13:27
    Chriztian Steinmeier
    0

    Hi Martin,

    I'm sure you also don't like the "messiness" of all the times you need to output "fake" div tags etc... there must be another way, right?

    Here's my take on this problem - although it may seem very different from what you'd normally see, it's a much cleaner way to handle stuff like this - ask me anything you want about it, if you want to understand what's going on.

    <xsl:param name="currentPage" />
    
    <!-- This is the number of initial items -->
    <xsl:variable name="offset" select="2" />
    
    <!-- This the number of nodes to group subsequently -->
    <xsl:variable name="groupSize" select="3" />
    
    <xsl:template match="/">
    
        <!-- Process the MNTP property -->
        <xsl:apply-templates select="$currentPage/homeFeatureBlock" />
    
    </xsl:template>
    
    <!-- Template for the MNTP property -->
    <xsl:template match="homeFeatureBlock">
        <!-- First group -->
        <div class="initial-row">
            <xsl:apply-templates select="MultiNodePicker/nodeId[position() &lt;= $offset]" />
        </div>
    
        <!-- Subsequent groups -->
        <xsl:for-each select="MultiNodePicker/nodeId[((position() + $offset - 1) mod $groupSize) = 1]">
            <div class="grouped-row">
                <xsl:apply-templates select=". | following-sibling::nodeId[position() &lt; $groupSize]" />
            </div>
        </xsl:for-each>
    </xsl:template>
    
    <!-- Helper to get the referenced node -->
    <xsl:template match="nodeId">
        <xsl:apply-templates select="id(.)" />
    </xsl:template>
    
    <!-- Generic template for a block -->
    <xsl:template match="*[@isDoc]">
        <div class="block">
            <h4><xsl:value-of select="@nodeName" /></h4>
            <p><xsl:value-of select="blockDescription" disable-output-escaping="yes"/></p>
        </div>
    </xsl:template>
    

    /Chriztian

  • Martin 278 posts 662 karma points
    Apr 26, 2013 @ 17:15
    Martin
    0

     

    Thanks Chriztian.

     

    It works great. I just have to understand it now. I have a few questions if you don't mind and a few problems I've ran into.

    I stripped out some code for posting the problem, and im having trouble fitting it back into the solution you gave me.

    The problem I'm having is that I need to apply a different class to the blocks depending on if its the first row or subsequent rows.

    So my block template would look like..

     

    <div class="2 column block"> for each of the "initial row" blocks, and <div class="3 column block"> for the subsequent blocks.

     

    Is there away to have two templates and set which template block to use?

    Im also having trouble with applying an image to the block I had.

    Im not all that competent with XSLT, so if you could help my understanding of what you did on a few points, thanks. Ive added my question to the code, if thats ok?

     

    Thanks for your help on this.

     

    <xsl:paramname="currentPage"/>

    <!-- This is the number of initial items -->
    <xsl:variablename="offset"select="2"/>

    <!-- This the number of nodes to group subsequently -->
    <xsl:variablename="groupSize"select="3"/>

    <xsl:templatematch="/">

           
    <!-- Process the MNTP property -->
           
    <xsl:apply-templatesselect="$currentPage/homeFeatureBlock"/>

    </xsl:template>

    <!-- Template for the MNTP property -->
    <xsl:templatematch="homeFeatureBlock">
           
    <!-- First group -->
           
    <divclass="initial-row">
                   
    <xsl:apply-templatesselect="MultiNodePicker/nodeId[position() &lt;= $offset]"/><!--OK, I GET THIS. IF POSITION OF NODE IS LESS THAN THE VARIABLE OFFSET.-->
           
    </div>

           
    <!-- Subsequent groups -->
           
    <xsl:for-eachselect="MultiNodePicker/nodeId[((position() + $offset - 1) mod $groupSize) = 1]"><!--IM SORRY, CAN YOU EXPLAIN THIS-->
                    <divclass="grouped-row">
                           
    <xsl:apply-templatesselect=". | following-sibling::nodeId[position() &lt; $groupSize]"/><!--AND THIS.-->
                   
    </div>
           
    </xsl:for-each>
    </xsl:template>

    <!-- Helper to get the referenced node -->
    <xsl:templatematch="nodeId">
           
    <xsl:apply-templatesselect="id(.)"/><!--IS THIS APPLYING THE TEMPLATE BELOW? WHAT EXACTLY ODES IT SAY IN LAYMENS TERMS?-->
    </xsl:template>

    <!-- Generic template for a block -->
    <xsl:templatematch="*[@isDoc]">
           
    <divclass="block">
    <xsl:variable name="image" select="blockImage[normalize-space()]"/> <xsl:choose> <xsl:when test="$image !=''"> <xsl:apply-templates select="$image" /> </xsl:when> <xsl:otherwise> <img src="/media/7832/defaultpixel.png"/> </xsl:otherwise> </xsl:choose>
                   
    <h4><xsl:value-ofselect="@nodeName"/></h4>
                   
    <p><xsl:value-ofselect="blockDescription"disable-output-escaping="yes"/></p>
    <!-- Output the URL using umbraco.library's NiceUrl method --> <a class="morelink" href="{umbraco.library:NiceUrl(.)}"><!--IM HAVING SOME TROUBLE HERE OUTPUTTING AN NICEURL--> <xsl:text>more information</xsl:text> </a>

           
    </div>
    </xsl:template>

    <!-- Image for block --> <xsl:template match="Image"> <img src="{umbracoFile}" alt="{@nodeName}" /> </xsl:template> 
     

     

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Apr 28, 2013 @ 00:04
    Chriztian Steinmeier
    0

     

    Hi Martin,

    A little about the tricky bits:

    <xsl:for-each select="MultiNodePicker/nodeId[((position() + $offset - 1) mod $groupSize) = 1]">

    MultiNodePicker/nodeId selects all the <nodeId> elements, which we will then filter...

    (position() + $offset - 1) gets us to the first one after the ones we started with.

    Using the modulos operator ((...) mod $groupSize) = 1 we select the 1st of every group of $groupSize elements following.

    Now we have a set of nodes that should all start a group, so we create the <div class="grouped-row"> and then we tell the processor to create a new set of nodes, combining the current one (.) and its following $groupSize siblings (to create the content inside the group):

    <xsl:apply-templates select=". | following-sibling::nodeId[position() &lt; $groupSize]" />

     

    This will execute the nodeId template for those items - because the <nodeId> elements are just references we need to get the actual Umbraco nodes - we could do this with umbraco.library:GetXmlNodeById() - but this will "explode" if a referenced node gets unpublished or deleted, so instead I use a built-in function - id(), which will just return an empty node set if the referenced node doesn't exist. (There's a couple of things that has to be right for this to work, but when used in Umbraco, inside a template for an element that exist in the XML, it's safe to use.)

    For the last one - outputting the URL - you just need to send in the @id attribute, because at this point you're inside a document template, so `.` will refer to the XML node itself - so:

    <a class="morelink" href="{umbraco.library:NiceUrl(@id)}">...</a>

     

    Hope that clears some confusion for you !

    /Chriztian

     

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Apr 28, 2013 @ 00:12
    Chriztian Steinmeier
    0

    Regarding the class names - first of all, "2" and "3" are not valid CSS class names, so you should be prepared for problems with those.

    You can send an additional parameter to the template that handles this, e.g. when rendering the "initial" blocks:

    <div class="initial-row">
        <xsl:apply-templates select="MultiNodePicker/nodeId[position() &lt;= $offset]">
            <xsl:with-param name="class" select="'column block c2'" />
        </xsl:apply-templates>
    </div>
    

    Then add the parameter to the template with a default value (so you don't need to change the 3-column version):

    <xsl:template match="*[@isDoc]">
        <xsl:param name="class" select="'column block c3'" /><!-- Default value -->
        <div class="{$class}">
            <!-- ... -->
        </div>
    </xsl:template>

    /Chriztian

  • Martin 278 posts 662 karma points
    Apr 29, 2013 @ 16:56
    Martin
    0

    Hi Chriztian, 

    Thanks again for the help. After a few reads, it's starting to make sense, especially using the built in function for id(). Ive had that "explore" problem before.
    My class names were really for illustration. I'll need to learn to publish exactly what i have infront of me.
    However Im still having an issue with apply the classes. 

     
    <!-- This is the number of initial items -->
    <xsl:variable name="offset" select="2" />
    <!-- This the number of nodes to group subsequently -->
    <xsl:variable name="groupSize" select="3" />
    <xsl:template match="/">
    <!-- Process the MNTP property -->
    <xsl:apply-templates select="$currentPage/homeFeatureBlock" />
    </xsl:template>
    <!-- Template for the MNTP property -->
    <xsl:template match="homeFeatureBlock">
    <!-- First group -->
    <div class="initial-row row clearfix">
            <xsl:apply-templates select="MultiNodePicker/nodeId[position() &lt;= $offset]">
                    <xsl:with-param name="class" select="'eight columns block'" />
            </xsl:apply-templates>
    </div>
    <!-- Subsequent groups -->
    <xsl:for-each select="MultiNodePicker/nodeId[((position() + $offset - 1) mod $groupSize) = 1]">
    <div class="grouped-row row clearfix">
    <xsl:apply-templates select=". | following-sibling::nodeId[position() &lt; $groupSize]" />
    </div>
    </xsl:for-each>
    </xsl:template>
    <!-- Helper to get the referenced node -->
    <xsl:template match="nodeId">
    <xsl:apply-templates select="id(.)" />
    </xsl:template>
     
    <!-- Generic template for a block -->
    <xsl:template match="*[@isDoc]">
            <xsl:param name="class" select="'one-third column block'" />
            <div class="{$class}">
    <xsl:variable name="image" select="blockImage[normalize-space()]"/>
    <xsl:choose>
    <xsl:when test="$image !=''">
    <xsl:apply-templates select="$image" />
    </xsl:when>
    <xsl:otherwise>
    <img src="/media/7832/defaultpixel.png"/>
    </xsl:otherwise>
    </xsl:choose>
    <h4><xsl:value-of select="@nodeName" /></h4>
    <p><xsl:value-of select="blockDescription" disable-output-escaping="yes"/></p>
     
    <!-- Output the URL using umbraco.library's NiceUrl method -->
    <a class="morelink" href="{umbraco.library:NiceUrl(@id)}">
    <xsl:text>more information</xsl:text>
    </a>
    </div>
    </xsl:template>
    <!-- Image for block -->
    <xsl:template match="Image">
    <img src="{umbracoFile}"  alt="{@nodeName}" />
    </xsl:template>

     

  • Martin 278 posts 662 karma points
    May 01, 2013 @ 10:24
    Martin
    0

    Sorry, just noticed that the styling of the code has gone weird.

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    May 01, 2013 @ 10:29
    Chriztian Steinmeier
    0

    Yeah that's a little hard to read :-)

    Ok - so what's you problem exactly? Can you show us what you want to get and what you are getting instead?

    /Chriztian

  • Martin 278 posts 662 karma points
    May 01, 2013 @ 11:35
    Martin
    0

     

     

    Hi Chriztian,

    Thanks again for your help.

    Sorry, my post doesn't seem to have the format option of "code", only preformated, which makes it go weird. Ive just left the code as text.

    I would like the first two nodes to be outputted like this

    <!-- FIRST 2 NODES -->

    <div class="initial-row row clearfix">

     <div class="eight columns block b-green">
       <img src="/block-one-image.png" alt="Block One" />
       <h4>Block One</h4>
    <a href="/block-one/">More</a>
     </div>


     <div class="eight columns block b-blue">
       <img src="/block-two-image.png" alt="Block Two" />
       <h4>Block Two</h4>
    <a href="/block-two/">More</a>
     </div>


    </div>

    And the remaining nodes to be outpuuted in groups of three.

    <!-- FIRST ROW OF 3-->

    <div class="grouped-row row clearfix">
     <div class="one-third column block b-red">
       <img src="/block-three-image.png" alt="Block Three" />
       <h4>Block Three</h4>
    <a href="/block-one/">More</a>
     </div>

     <div class="one-third column block b-yellow">
       <img src="/block-four-image.png" alt="Block Four" />
       <h4>Block Four</h4>
    <a href="/block-four/">More</a>
     </div>
      <div class="one-third column block b-purple">
       <img src="/block-five-image.png" alt="Block Five" />
       <h4>Block Five</h4>
    <a href="/block-five/">More</a>
    </div> 

    </div>

    <!-- SECOND ROW OF 3-->

    <div class="grouped-row row clearfix">
     <div class="one-third column block b-white">
       <img src="/block-six-image.png" alt="Block Six" />
       <h4>Block Six</h4>
    <a href="/block-six/">More</a>
     </div>
     <div class="one-third column block b-orange">
       <img src="/block-seven-image.png" alt="Block Seven" />
       <h4>Block Seven</h4>
    <a href="/block-seven/">More</a>
     </div>
      <div class="one-third column block b-black">
       <img src="/block-eight-image.png" alt="Block Eight" />
       <h4>Block Eight</h4>
    <a href="/block-eight/">More</a>
     </div> 

    </div>

     

     

    What is happening is that the "initial-row" blocks are outputting with the class of "eight columns", instead of "one-third column".

    The XSLT I have is

    <xsl:param name="currentPage" />

     

    <!-- This is the number of initial items -->

    <xsl:variable name="offset" select="2" />

     

    <!-- This the number of nodes to group subsequently -->

    <xsl:variable name="groupSize" select="3" />

     

    <xsl:template match="/">

     

    <!-- Process the MNTP property -->

    <xsl:apply-templates select="$currentPage/homeFeatureBlock" />

     

    </xsl:template>

     

    <!-- Template for the MNTP property -->

    <xsl:template match="homeFeatureBlock">

     

    <!-- First group -->

    <div class="initial-row row clearfix">

     

           <xsl:apply-templates select="MultiNodePicker/nodeId[position() &lt;= $offset]" >

                 <xsl:with-param name="class" select="'eight columns block'" />   

           </xsl:apply-templates>

    </div>

     

    <!-- Subsequent groups -->

    <xsl:for-each select="MultiNodePicker/nodeId[((position() + $offset - 1) mod $groupSize) = 1]">

    <div class="grouped-row row clearfix">

    <xsl:apply-templates select=". | following-sibling::nodeId[position() &lt; $groupSize]" />

    </div>

    </xsl:for-each>

    </xsl:template>

     

    <!-- Helper to get the referenced node -->

    <xsl:template match="nodeId">

    <xsl:apply-templates select="id(.)" />

    </xsl:template>

     

    <!-- Generic template for a block -->

    <xsl:template match="*[@isDoc]">

            <xsl:param name="class" select="'one-third column block'" />

            <div class="{$class}">

    <xsl:variable name="image" select="blockImage[normalize-space()]"/>

    <xsl:choose>

    <xsl:when test="$image !=''">

    <xsl:apply-templates select="$image" />

    </xsl:when>

    <xsl:otherwise>

    <img src="/media/7832/defaultpixel.png"/>

    </xsl:otherwise>

    </xsl:choose>

    <h4><xsl:value-of select="@nodeName" /></h4>

    <p><xsl:value-of select="blockDescription" disable-output-escaping="yes"/></p>

     

    <!-- Output the URL using umbraco.library's NiceUrl method -->

    <a class="morelink" href="{umbraco.library:NiceUrl(@id)}">

    <xsl:text>more information</xsl:text>

    </a>

     

     

    </div>

    </xsl:template>

     

    <!-- Image for block -->

    <xsl:template match="Image">

    <img src="{umbracoFile}"  alt="{@nodeName}" />

    </xsl:template>

     

Please Sign in or register to post replies

Write your reply to:

Draft