Recursive menu Options
marcus.stenbeck
Posted: Monday, July 28, 2008 11:11:53 AM
Rank: Newbie

Joined: 7/28/2008
Posts: 6
Location: Sweden
Hi, i'm new to the forum and to Umbraco. What I'm trying to do is to make a nested navigation. I've used the sample Navigation Prototype and modified it a little.

Basically what I'm trying to figure out is how you write a recursive function that'll show a menu item's direct children when it's the selected node.

Quote:

Example. Bold items are "selected".

Home
Services
-- Programming
---- XHTML
---- PHP
---- .NET
------ C#
------ VB
---- CSS
-- Design
-- Web optimisation
About
Contact



This is my code...
Code:

<xsl:template match="/">

<!-- The fun starts here -->

<ul>
  <xsl:for-each select="$currentPage/ancestor-or-self::node [@level=$level]/node [string(data [@alias='umbracoNaviHide']) != '1']">
    <li>
      <a href="{umbraco.library:NiceUrl(@id)}">
        <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
          <xsl:attribute name="class">nav_selected</xsl:attribute>
        </xsl:if>
        <xsl:value-of select="@nodeName"/>
      </a>
     
      <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
        <xsl:if test="$currentPage/ancestor-or-self::node/descendant::node">
        <ul>
          <xsl:for-each select="$currentPage/ancestor-or-self::node [@level=$level+1]/node [string(data [@alias='umbracoNaviHide']) != '1']">
            <li>
              <a href="{umbraco.library:NiceUrl(@id)}">
                <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">

                  <xsl:attribute name="class">nav_selected</xsl:attribute>
                </xsl:if>
                <xsl:value-of select="@nodeName"/>
              </a>
            </li>
          </xsl:for-each>
        </ul>
        </xsl:if>
      </xsl:if>
     
    </li>
  </xsl:for-each>
</ul>

</xsl:template>


It's probably an easy fix, but as I'm new to XSLT, XPath, XQuery, Xetcetera it's pretty alien to me, so I'd appreciate any help. Thanks!
Christian Jansen
Posted: Monday, July 28, 2008 12:33:50 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 10
Location: Stavanger, Norway
Maybe this will work for you. Haven't tested it a lot, but it seems to work for me.

Code:

<xsl:template match="/">

<!-- The fun starts here -->
<xsl:call-template name="menu"><xsl:with-param name="level" select="$level"/></xsl:call-template>


</xsl:template>


<xsl:template name="menu">

    <xsl:param name="level"/>

        <xsl:if test="$currentPage/ancestor-or-self::node/descendant::node">
        <ul>
          <xsl:for-each select="$currentPage/ancestor-or-self::node [@level=$level]/node [string(data [@alias='umbracoNaviHide']) != '1']">
            <li>
              <a href="{umbraco.library:NiceUrl(@id)}">
                <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
                  <xsl:attribute name="class">nav_selected</xsl:attribute>
                </xsl:if>
                <xsl:value-of select="@nodeName"/>
              </a>
                <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
                    <xsl:call-template name="menu"><xsl:with-param name="level" select="$level+1"/></xsl:call-template>
                </xsl:if>
            </li>
          </xsl:for-each>
        </ul>
        </xsl:if>


</xsl:template>
marcus.stenbeck
Posted: Monday, July 28, 2008 1:33:46 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 6
Location: Sweden
Yeah, that works a little better, but I have a problem - if there's no descendants of the selected node it'll place the menu items that are supposed to be on the same level as children.

Quote:

This is how I want it to be. Bold items are "selected".

Home
Services
-- Programming
---- XHTML
---- PHP
---- .NET
------ C#
------ VB
---- CSS
-- Design
-- Web optimisation
About
Contact



Quote:

This is how it turns out. Bold items are "selected".

Home
Services
-- Programming
---- XHTML
---- PHP
---- .NET
------ C#
------ VB
------ CSS
---- Design
---- Web optimisation
-- About
-- Contact


As you can see all of the items below the selected item (that has no descendants) are bumped one "menu level".


marcus.stenbeck
Posted: Monday, July 28, 2008 2:10:02 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 6
Location: Sweden
I think that the problem lies in that
Code:
<xsl:if test="$currentPage/ancestor-or-self::node/descendant::node">
seems to return true for one level deeper than it should.

Any thoughts?
Christian Jansen
Posted: Monday, July 28, 2008 2:10:47 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 10
Location: Stavanger, Norway
I see.. thats a little bug because of an empty list.

How about this:

Code:

<xsl:template name="menu">

    <xsl:param name="level"/>

        <xsl:if test="count($currentPage/ancestor-or-self::node [@level=$level]/node [string(data [@alias='umbracoNaviHide']) != '1']) &gt; '0'">
        <ul>
          <xsl:for-each select="$currentPage/ancestor-or-self::node [@level=$level]/node [string(data [@alias='umbracoNaviHide']) != '1']">
            <li>
              <a href="{umbraco.library:NiceUrl(@id)}">
                <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
                  <xsl:attribute name="class">nav_selected</xsl:attribute>
                </xsl:if>
                <xsl:value-of select="@nodeName"/>
              </a>
                <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id">
                    <xsl:call-template name="menu"><xsl:with-param name="level" select="$level+1"/></xsl:call-template>
                </xsl:if>
            </li>
          </xsl:for-each>
        </ul>
        </xsl:if>


</xsl:template>



Does that work properly?
marcus.stenbeck
Posted: Monday, July 28, 2008 2:18:42 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 6
Location: Sweden
Yes, thank you!

Could you explain which lines you edited and what the difference was? I'd appreciate it!
Christian Jansen
Posted: Monday, July 28, 2008 2:32:48 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 10
Location: Stavanger, Norway
marcus.stenbeck wrote:
Could you explain which lines you edited and what the difference was? I'd appreciate it!


I edited the very line that you pointed out for me. I'm not sure what that test-clause meant before, but now it is true if and only if there are more than 0 children of the "current" node. I use count() to count the children, and &gt; '0' means "greater than 0".

So now, if the current node has children, then we'll go on and make a list of them. If current node is child-free, then we don't bother making an empty ul-tag.
marcus.stenbeck
Posted: Monday, July 28, 2008 2:34:23 PM
Rank: Newbie

Joined: 7/28/2008
Posts: 6
Location: Sweden
Thanks a lot. I just wanted to make sure I didn't miss anything.

Cheers! Applause
jonhoye
Posted: Sunday, September 07, 2008 6:26:04 PM
Rank: Newbie

Joined: 9/5/2008
Posts: 9
Location: Chattanooga, TN
Very Helpful. I just used this post to set up a chromemenu. The MenuItem variable is a true/false box added to the templates, can be removed.

<xsl:template match="/">
<div class="chromestyle" id="topmenu">
<ul>
<xsl:for-each select="$currentPage/ancestor::root//node">
<xsl:if test="data [@alias = 'MenuItem'] &gt; 0">
<xsl:if test="@level &lt; 2">
<li>
<a href="{umbraco.library:NiceUrl(current()/@id)}">
<xsl:if test="count((current())/node) &gt; '0'">
<xsl:attribute name="rel">menudiv<xsl:value-of select="(current()/@id)" /></xsl:attribute>
</xsl:if>
<xsl:value-of select="@nodeName"/>
</a>
</li>
</xsl:if>
</xsl:if>
</xsl:for-each>
</ul>
</div>


<xsl:for-each select="$currentPage/ancestor::root//node">
<xsl:if test="data [@alias = 'MenuItem'] &gt; 0">
<xsl:if test="@level = 1">
<xsl:if test="count((current())/node) &gt; '0'">
<div id="menudiv{(current()/@id)}" class="dropmenudiv" style="width:150px;">
<xsl:for-each select="(current())/node">
<xsl:if test="data [@alias = 'MenuItem'] &gt; 0">
<xsl:if test="@level = 2">
<a href="{umbraco.library:NiceUrl(current()/@id)}"><xsl:value-of select="@nodeName"/></a>
</xsl:if>
</xsl:if>
</xsl:for-each>
</div>
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:for-each>

<script type="text/javascript">
cssdropdown.startchrome("topmenu")
</script>

</xsl:template>
azzlack
Posted: Monday, September 08, 2008 11:17:29 PM

Rank: Devotee

Joined: 8/5/2007
Posts: 95
Location: Bergen, Norway
How would you do this of you wanted all the children displayed at once, not when you click the parent node?

www.eyecatch.no
bwakkie
Posted: Thursday, September 11, 2008 9:28:45 AM

Rank: Devotee

Joined: 7/6/2007
Posts: 69
Location: Brussels
azzlack wrote:
How would you do this of you wanted all the children displayed at once, not when you click the parent node?


<xsl:if test="$currentPage/ancestor-or-self::node/descendant::node">?

^(B(astia{2}n)?)(\s)?(W(ak{2}ie)?)$ IRC --> irc://irc.freenode.org/umbraco
Users browsing this topic
Guest


You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.