Copied to clipboard

Flag this post as spam?

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


  • Bo Damgaard Mortensen 719 posts 1207 karma points
    Jul 11, 2011 @ 11:15
    Bo Damgaard Mortensen
    0

    Rendering structure in a select element

    Hi all,

    I'm having a bit of trouble when trying to render a part of my sites structure in a html select box. My structure looks like this:

    - Root node
      - Node
          - ChildNode
          - ChildNode
          - ChildNode
          - ...
      - Node
          - ChildNode
          - ChildNode
          - ...
      - ...

    and my select box should be like this:

    <select>
      <optgroup value="{$Node}">
          <option>$ChildNode</option>
    <option>$ChildNode</option> </optgroup>

    I've got the following XSLT at the moment:

    <xsl:template match="/">
        <xsl:variable name="siteNode" select="/macro/siteRootNode" />
        <xsl:if test="$siteNode != ''">
          <select>
          <xsl:for-each select="$siteNode/* [@isDoc]">
            <optgroup label="{@nodeName}">
                <xsl:call-template name="getChildNodes">
                  <xsl:with-param name="childNodes" select="./*" />
                </xsl:call-template>
              </optgroup>
          </xsl:for-each>
          </select>
        </xsl:if>
      </xsl:template>
      <xsl:template name="getChildNodes">
        <xsl:param name="childNodes"></xsl:param>
        <xsl:for-each select="$childNodes">
          <option><xsl:value-of select="@nodeName"/></option>
        </xsl:for-each>
      </xsl:template>

    This "works", but since it's basically a nested for loop (which is disgusting in itself!) it creates an empty option for every parent node.

    Anyone got any hints on this? :-) Will be greatly appreciated!

    All the best,

    Bo

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Jul 11, 2011 @ 11:28
    Chriztian Steinmeier
    0

    Here's something to get you going with templates:

    <xsl:template match="/">
        <select>
            <xsl:apply-templates select="$siteNode/NODE_DOCTYPE_ALIAS" />
        </select>
    </xsl:template>
    
    <xsl:template match="NODE_DOCTYPE_ALIAS">
        <optgroup label="{@nodeName}">
            <xsl:apply-templates select="CHILDNODE_DOCTYPE_ALIAS" />
        </optgroup>
    </xsl:template>
    
    <xsl:template match="CHILDNODE_DOCTYPE_ALIAS">
        <option>
            <xsl:value-of select="@nodeName" />
        </option>
    </xsl:template>

    /Chriztian

  • Bo Damgaard Mortensen 719 posts 1207 karma points
    Jul 11, 2011 @ 11:59
    Bo Damgaard Mortensen
    0

    Hi Chriztian,

    Thanks a lot for your reply :-) While it makes sense to me with those three templates, I'm still a bit on the blank side when it comes to actually getting any values in the select element. Do you have a hint for where/when to loop through the node collection? 

    Thanks again!

    - Bo

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Jul 11, 2011 @ 14:25
    Chriztian Steinmeier
    1

    Hi Bo,

    Imagine a structure like this:

    <root>
        <Website nodeName="Home">
            <Textpage nodeName="About"></Textpage>
            <Textpage nodeName="Products"></Textpage>
            <Textpage nodeName="Terms &amp; Conditions"></Textpage>
            <!-- More pages -->
    
            <Categories nodeName="Tags">
                <Categories nodeName="Web">
                    <Category>XML</Category>
                    <Category>XSLT</Category>
                    <Category>Razor</Category>
                </Categories>
    
                <Categories nodeName="CMS">
                    <Category nodeName="Umbraco"></Category>
                    <Category nodeName="Sitecore"></Category>
                    <Category nodeName="Synkron Via"></Category>
                </Categories>
    
                <!-- More Categories -->
            </Categories>
    
        </Website>
    </root>

    Then I'd do something like this in the macro:

    <?xml version="1.0" encoding="utf-8" ?>
    <xsl:stylesheet
        version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:umb="urn:umbraco.library"
        exclude-result-prefixes="umb"
    >
    
        <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
    
        <xsl:param name="currentPage" />
        <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />
    
        <!-- Grab the id of the Tags wrapper from a macro parameter -->
        <xsl:variable name="tagsNode" select="/macro/tagsNode" />
    
        <xsl:template match="/">
            <select>
                <!-- Find the Tags wrapper and process the Categories elements inside -->
                <xsl:apply-templates select="$siteRoot//Categories[@id = $tagsNode]/Categories" />
            </select>
        </xsl:template>
    
        <xsl:template match="Categories">
            <optgroup label="{@nodeName}">
                <xsl:apply-templates select="Category" />
            </optgroup>
        </xsl:template>
    
        <xsl:template match="Category">
            <option>
                <xsl:value-of select="@nodeName" />
            </option>
        </xsl:template>
    
    </xsl:stylesheet> 

    This assumes your "Home" node is at level 1 (very common, you know).

    ** If you know the "Tags" node is a direct child of the Home node, remove the extra slash, so the apply-templates instruction goes like this:

    <xsl:apply-templates select="$siteRoot/Categories[@id = $tagsNode]/Categories" /> 

    - which will be faster...

    Let me know if you still need help getting it to work.

    /Chriztian

  • Bo Damgaard Mortensen 719 posts 1207 karma points
    Jul 12, 2011 @ 11:54
    Bo Damgaard Mortensen
    0

    Hi Chriztian and thanks again for your input! :-) greatly appreciated!

    I'm having this code now, but I'm not sure the optgroup template is ever called(?)

     

    <xsl:param name="currentPage"/>
    <xsl:variable name="root" select="umbraco.library:GetXmlNodeById(1071)" />
    <xsl:template match="/">
           <select>
    <xsl:apply-templates select="$root/*"></xsl:apply-templates>
    </select>
    </xsl:template>
    <xsl:template match="Countries">
    <optgroup label="{@nodeName}">
    <xsl:apply-templates select="Destinations"></xsl:apply-templates>
    </optgroup>
    </xsl:template>
    <xsl:template match="Destinations">
    <option>
    <xsl:value-of select="@nodeName"/>
    </option>
    </xsl:template>

    Seems I'm having some serious trouble grasping the concept of templates ;-) I'm sure I'm doing something wrong here?

    Thanks again,

    - Bo

     

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Jul 12, 2011 @ 12:34
    Chriztian Steinmeier
    0

    Hi Bo,

    Good to see you hangin' in there :-)

    If your $root node (1071) is a wrapper for the <Countries> nodes, then it should work (and it's best to say $root/Countries intead of $root/* because the latter will process properties of the $root node too, if any, which may give you some extra unintended output).

    My guess would be that there's maybe an extra node between $root and the Countries elements (e.g. $root/Settings/Countries) ?

    /Chriztian

  • Bo Damgaard Mortensen 719 posts 1207 karma points
    Jul 12, 2011 @ 13:55
    Bo Damgaard Mortensen
    0

    Hi again Chriztian,

    There's no extra node/level between the root node and the countries node. The problem was that the Countries template was never called. Had to use mode="Countries" for that :-) However, I can't get the Destination template to work, which is quite weird because i'm calling it the same way as the Countries template.

    My code so far (have to replace the GetXmlNodeById() with a parameter though ;-))

      <xsl:variable name="root" select="umbraco.library:GetXmlNodeById(1071)" />

      <xsl:template match="/">

        <select>

          <option>Vælg destination</option>

          <xsl:apply-templates select="$root/Page" mode="Countries"></xsl:apply-templates>

        </select>

      </xsl:template>

      <xsl:template match="*" mode="Countries">

        <option label="{@nodeName}">

          <xsl:apply-templates select="./Destination" mode="Destinations"></xsl:apply-templates>

        </option>

      </xsl:template>

      <xsl:template match="*" mode="Destinations">

        <option>

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

        </option>

      </xsl:template>

    Sorry about the formatting - seems we're missing the 'Code' option in the RTE :-/

    So, going the the above code, shouldn't I be able to call the Destinations template with the current Country node by: ./Destination ? Or have I missed something?

    Thanks again!

    - Bo

  • Bo Damgaard Mortensen 719 posts 1207 karma points
    Jul 12, 2011 @ 15:53
    Bo Damgaard Mortensen
    0

    Alright, finally got it.. and I'm so unbelievable embarrassed about the mistake ;-)

    The final solution:

    <xsl:template match="/">

        <select class="destination-dropdown">

          <option>Vælg destination</option>

          <xsl:apply-templates select="$root/Page" mode="Countries"></xsl:apply-templates>

        </select>

      </xsl:template>

      <xsl:template match="*" mode="Countries">

        <option value="{umbraco.library:NiceUrl(@id)}" class="dd-country-name" label="{@nodeName}">

          <xsl:apply-templates select="./destination" mode="Destinations"></xsl:apply-templates>

        </option>

      </xsl:template>

      <xsl:template match="*" mode="Destinations">

        <option value="{umbraco.library:NiceUrl(@id)}">

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

        </option>

      </xsl:template>

    My grand mistake was to spell "destinations" with lowercase and not "Destinations" /major-golfclap.

    Thanks Chriztian for your help and patience! ;-)

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Jul 13, 2011 @ 22:58
    Chriztian Steinmeier
    0

    Hi Bo,

    You're welcome - no worries.

    I'd just like to clarify the use of the mode attribute vs. the match attribute on the template element:

    The match attribute needs to reference an element name from the XML, so in your case you could say:

    <xsl:template match="Page">

    instead of:

     <xsl:template match="*" mode="Countries">

    and then take the mode="Countries" out of the apply-templates statement.

    Likewise for the "destinations" template, of course.

    The mode attribute is normally used when you need more than one template for the same element, or when a template could be used for any element (which is where you'd normally use match="*" mode="something-clever").

    So just to be hyper-pedantic, I'll leave you with this (changed stuff highlighted):

    <xsl:template match="/">
        <select class="destination-dropdown">
            <option>Vælg destination</option>
            <xsl:apply-templates select="$root/Page" />
        </select>
    </xsl:template>
    
    <xsl:template match="Page">
        <option value="{umbraco.library:NiceUrl(@id)}" class="dd-country-name" label="{@nodeName}">
            <xsl:apply-templates select="destination" />
        </option>
    </xsl:template>
    
    <xsl:template match="destination">
        <option value="{umbraco.library:NiceUrl(@id)}">
            <xsl:value-of select="@nodeName" />
        </option>
    </xsl:template>
    

    /Chriztian

  • Bo Damgaard Mortensen 719 posts 1207 karma points
    Jul 14, 2011 @ 23:16
    Bo Damgaard Mortensen
    0

    Thank you so much for clarifying, Chriztian! :-) Never really got my head around templates in XSLT, but your example above makes it absolutely clear - feels great to finally grasp it.

    Now.. back to razor and umbraco 4.7! ;-) /teasing

    - Bo

Please Sign in or register to post replies

Write your reply to:

Draft