Copied to clipboard

Flag this post as spam?

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


  • Tony Strack 16 posts 96 karma points
    Mar 15, 2018 @ 22:48
    Tony Strack
    0

    XSLT Multi Level Nav

    I'm new to Umbraco and trying to build a simple UL menu with child pages that show under the current page. For example:

    <ul class="nav">
        <li><a href="/church/">Church</a></li>
        <li><a href="#">School</a>
            <ul>
                <li><a href="/school/about/">About</a></li>
                <li><a href="/school/academics/">Academics</a></li>
                <li><a href="/school/admissions/">Admissions</a></li>
                <li><a href="/school/after-school/">After School</a></li>
                <li><a href="/school/alumni/">Alumni</a></li>
                <li><a href="/school/service/">Service</a></li>
            </ul>
        </li>
        <li><a href="/faith-development/">Faith Development</a></li>
        <li><a href="/visits-tours/">Visits &amp; Tours</a></li>
        <li><a href="/youth-ministries/">Youth Ministries</a></li>
        <li><a href="/blog/">Blog</a></li>
    </ul>
    

    Here is my XSLT

    <!-- update this variable on how deep your site map should be -->
    <xsl:variable name="maxLevelForSitemap" select="4"/>
    
    <xsl:template match="/">
        <div class="nav-super"> 
            <xsl:call-template name="drawNodes">  
            <xsl:with-param name="parent" select="$currentPage/ancestor-or-self::* [@isDoc and @level=1]"/>
            </xsl:call-template>
        </div>
    </xsl:template>
    
    <xsl:template name="drawNodes">
        <xsl:param name="parent"/> 
        <xsl:if test="umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1 and umbraco.library:IsLoggedOn() = 1)">
            <ul>
                <xsl:for-each select="$parent/* [@isDoc and string(umbracoNaviHide) != '1' and @level &lt;= $maxLevelForSitemap]"> 
                    <li>  
                        <a href="{umbraco.library:NiceUrl(@id)}">
                            <xsl:value-of select="@nodeName"/>
                        </a>  
                        <xsl:if test="count(./* [@isDoc and string(umbracoNaviHide) != '1' and @level &lt;= $maxLevelForSitemap]) &gt; 0"> 
                            <xsl:call-template name="drawNodes">    
                                <xsl:with-param name="parent" select="."/>    
                            </xsl:call-template>  
                        </xsl:if> 
                    </li>
                </xsl:for-each>
            </ul>
        </xsl:if>
    </xsl:template>
    

    However, when I'm on the Church page, that doesn't have children, the child pages of School show. I only want those to show when I'm on the School page.

    Any help would be greatly appreciated. Thanks!

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    Mar 16, 2018 @ 07:42
    Dave Woestenborghs
    0

    Hi Tony,

    I just looked at an old site I have lying around that still uses XSLT.

    I have a parameter in my xslt like this

    <xsl:param name="currentPage"/>
    

    I think this get's auto populated by Umbraco, because I don't see it being set in the template where I call the macro

    And then to test if i'm on the current page I do this. It checks if the id of the currentPage param is equal to the current one from the in the for-each

    <xsl:if test="$currentPage/@id=current()/@id">
                    <!-- your logic here -->
                </xsl:if>
    

    Dave

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    Mar 16, 2018 @ 07:50
    Dave Woestenborghs
    0

    According to the docs the currentPage parameter get's populated by Umbraco

    https://our.umbraco.org/documentation/reference/templating/macros/xslt/The-Basics

  • David Zweben 268 posts 754 karma points
    Mar 17, 2018 @ 13:46
    David Zweben
    0

    Is there a specific reason you're using XSLT if you're new to Umbraco? XSLT is more of a legacy language for Umbraco at this point, you're much better off working in C# if you plan to work on new Umbraco sites.

  • Tony Strack 16 posts 96 karma points
    Mar 17, 2018 @ 21:25
    Tony Strack
    1

    Primarily due to the fact that this site and the majority of the other sites I'm working with use XSLT for nearly everything. I won't be building new Umbraco sites, rather working with legacy sites on Umbraco 7 and below.

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    Mar 18, 2018 @ 09:54
    Dave Woestenborghs
    0

    Hi Tony,

    Did my earlier reply help you out ?

    Dave

  • Tony Strack 16 posts 96 karma points
    Mar 18, 2018 @ 12:54
    Tony Strack
    0

    Yes, Dave! It got me half way there. The only problem now is I still need to get the 3rd level to show below the current page. Right now it's displaying 1st level for all pages & 2nd levels below the current page, but not the 3rd. I still need to have the nav show the 3rd level below the current page. Here's my current code:

    ]>

    <!-- update this variable on how deep your site map should be -->
    <xsl:variable name="maxLevelForSitemap" select="4"/>
    
    <xsl:template match="/">
    
        <div class="nav-desktop"> 
            <xsl:call-template name="drawNodes">  
            <xsl:with-param name="parent" select="$currentPage/ancestor-or-self::* [@isDoc and @level=1]"/>
            </xsl:call-template>
        </div>
    
    </xsl:template>
    
    <xsl:template name="drawNodes">
        <xsl:param name="parent"/> 
        <xsl:if test="umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1 and umbraco.library:IsLoggedOn() = 1)">
            <ul>
                <xsl:for-each select="$parent/* [@isDoc and string(umbracoNaviHide) != '1' and @level &lt;= $maxLevelForSitemap]"> 
                    <li>
                        <a href="{umbraco.library:NiceUrl(@id)}">
                            <xsl:value-of select="@nodeName"/>
                        </a>
                        <xsl:if test="$currentPage/@id=current()/@id">
                            <xsl:call-template name="drawNodes">    
                                <xsl:with-param name="parent" select="."/>    
                            </xsl:call-template>  
                        </xsl:if>
                    </li>
                </xsl:for-each>
            </ul>
        </xsl:if>
    </xsl:template>
    

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    Mar 18, 2018 @ 13:40
    Dave Woestenborghs
    0

    Maybe you should set the maxLevelForSitemap one higher ?

    Dave

  • Tony Strack 16 posts 96 karma points
    Mar 18, 2018 @ 16:21
    Tony Strack
    0

    Nope, that didn't do it.

  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    Mar 19, 2018 @ 09:14
    Dave Woestenborghs
    0

    Hi Tony,

    I think this check is the culprit :

    <xsl:if test="$currentPage/@id=current()/@id">
    

    It will only render items when the current item on level 1 is the same as the visited page.

    So this check needs to be extended. I have been looking in some xslt files from a site that still runs v4 of umbraco

    Add this at the top of your xslt :

    <xsl:variable name="currentPath" select="concat($currentPage/@path,',')" />
    

    And then update the test like this :

    <xsl:if test="contains($currentPath,@id)">
    

    Let me know if that works.

    Dave

  • Tony Strack 16 posts 96 karma points
    Mar 19, 2018 @ 12:42
    Tony Strack
    0

    Thanks for your help, Dave. I appreciate it. However, still no luck displaying the 3rd level. Here's the updated code per your suggestions:

        <!-- update this variable on how deep your site map should be -->
        <xsl:variable name="maxLevelForSitemap" select="4"/>
        <xsl:variable name="currentPath" select="concat($currentPage/@path,',')" />
    
        <xsl:template match="/">
    
            <div class="nav-desktop"> 
                <xsl:call-template name="drawNodes">  
                <xsl:with-param name="parent" select="$currentPage/ancestor-or-self::* [@isDoc and @level=1]"/>
                </xsl:call-template>
            </div>
    
        </xsl:template>
    
        <xsl:template name="drawNodes">
            <xsl:param name="parent"/>
            <xsl:if test="umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1 and umbraco.library:IsLoggedOn() = 1)">
                <ul>
                    <xsl:for-each select="$parent/* [@isDoc and string(umbracoNaviHide) != '1' and @level &lt;= $maxLevelForSitemap]"> 
                        <li>
                            <a href="{umbraco.library:NiceUrl(@id)}">
                                <xsl:value-of select="@nodeName"/>
                            </a>
                            <xsl:if test="contains($currentPath,@id)">
                                <xsl:call-template name="drawNodes">    
                                    <xsl:with-param name="parent" select="."/>    
                                </xsl:call-template>  
                            </xsl:if>
                        </li>
                    </xsl:for-each>
                </ul>
            </xsl:if>
        </xsl:template>
    
  • Dave Woestenborghs 3504 posts 12135 karma points MVP 9x admin c-trib
    Mar 19, 2018 @ 13:04
    Dave Woestenborghs
    0

    I'm going to call in some help from a xslt expert.

    Dave

  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Mar 19, 2018 @ 14:21
    Chriztian Steinmeier
    1

    Hi Tony,

    Here's my take - and I know it's a complete rewrite, but it's ususally easier to tweak, than the "drawNodes" approach you're trying to tweak:

    <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="html" indent="yes" omit-xml-declaration="yes" />
    
        <xsl:param name="currentPage" />
        <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />
    
        <!-- Select the children of the Home/Site (level 1) as first level in navigation -->
        <xsl:variable name="rootLevelNodes" select="$siteRoot/*[@isDoc][not(umbracoNaviHide = 1)]" />
    
        <xsl:template match="/">
            <ul class="nav">
                <!-- Process the selected nodes here -->
                <xsl:apply-templates select="$rootLevelNodes" />
            </ul>
        </xsl:template>
    
        <xsl:template match="*[@isDoc]">
            <!-- Select any children of this one -->
            <xsl:variable name="childNodes" select="*[@isDoc][not(umbracoNaviHide = 1)]" />
    
            <li>
                <a href="{umb:NiceUrl(@id)}">
                    <xsl:value-of select="@nodeName" />
                </a>
                <!-- If $currentPage is either this one or below, render any children as subnavigation -->
                <xsl:if test="$childNodes and descendant-or-self::*[@id = $currentPage/@id]">
                    <ul>
                        <xsl:apply-templates select="$childNodes" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
    
    </xsl:stylesheet>
    

    This initial version does not handle protected pages, nor does it set a maximum level - I rarely need those, and I never add them in before I need them :)

    There are only two templates - the one that handles output and the one that sets it off.

    Hope that helps - and let me know if it fails/explodes/works :)

    /Chriztian

  • Tony Strack 16 posts 96 karma points
    Mar 19, 2018 @ 15:30
    Tony Strack
    0

    So very close, Chriztian. The 3rd level displays when you're on a 3rd level page, but not when you're on a 2nd level page. I'd like for the 3rd level to display on 2nd level pages as well.

  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Mar 19, 2018 @ 15:46
    Chriztian Steinmeier
    0

    Great - just to be sure: it does display 3rd level if the 2nd level page you’re on has children, right?

  • Tony Strack 16 posts 96 karma points
    Mar 19, 2018 @ 16:03
    Tony Strack
    0

    When you're on a 2nd level page, it does not display the 3rd level if the 2nd level page has children.

    It does, however, display the 3rd level when on the 3rd level page.

    Here's a visual:

    http://2018.stjn.org.172-24-16-245.leinteractive.com/school/ - 2nd level page - missing drop down below Academics

    http://2018.stjn.org.172-24-16-245.leinteractive.com/school/academics/ - 3rd level page - drop down is properly showing below Academics. I'd like this dropdown to also show on 2nd level pages.

  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Mar 19, 2018 @ 16:12
    Chriztian Steinmeier
    0

    I can’t believe it ;)

    Can you post the code? Something’s not right - if I run that on a standard structure it works as intended ...

    /Chriztian

  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Mar 19, 2018 @ 16:17
    Chriztian Steinmeier
    0

    Actually, now I get it - you want 4th level pages to show when you’re on a 2nd level page, which this does not do... ?

  • Tony Strack 16 posts 96 karma points
    Mar 19, 2018 @ 16:45
    Tony Strack
    0

    I suppose that is technically the 4th level. ;)

    Say I'm on /school. Currently, it displays /school/academics in the nav. But I need it display 1 level deeper. i.e. /school/academics/preschool.

    When I go to /school/academics, it is showing /school/academics/preschool in the nav.

  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Mar 19, 2018 @ 20:39
    Chriztian Steinmeier
    0

    No problem :)

    You want to test the ancestor axis too then, like this:

    <!-- If $currentPage is either above or below, render any children as sub navigation -->
    <xsl:if test="$childNodes and (ancestor::*[@id = $currentPage/@id] or descendant-or-self::*[@id = $currentPage/@id])">
    

    But you need to decide a couple of things:

    1. What happens on the Home (/) page? - How many levels do you want to show there? (If we just look up the ancestor axis we'll get a match on every page in the navigation when we're on the home page).

    2. What happens on the /school/about/ page - do you want to expand Academics there as well? The academics branch is neither on the ancestors or descendants axis so need to maybe look at the level?

    /Chriztian

    (This is exactly why I always write navigations from scratch - there's always something different :-)

  • Tony Strack 16 posts 96 karma points
    Mar 19, 2018 @ 21:25
    Tony Strack
    0

    Now it's really real close :)

    1. I hid the sub pages on the home page via CSS, so all good there.
    2. You raise a great question. Is there a way to show all pages beneath /school when you're on any sub page of /school, regardless of the level?
  • Chriztian Steinmeier 2800 posts 8791 karma points MVP 8x admin c-trib
    Mar 19, 2018 @ 21:39
    Chriztian Steinmeier
    100

    Alrighty!

    Then here's a full sheet to bring everyone up to speed:

    <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="html" indent="yes" omit-xml-declaration="yes" />
    
        <xsl:param name="currentPage" />
        <xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />
    
        <!-- Select the children of the Home/Site (level 1) as first level in navigation -->
        <xsl:variable name="rootLevelNodes" select="$siteRoot/*[@isDoc][not(umbracoNaviHide = 1)]" />
    
        <!-- Which section are we in (if any?) -->
        <xsl:variable name="currentSection" select="$currentPage/ancestor-or-self::*[@level = 2]" />
    
        <xsl:template match="/">
            <ul class="nav">
                <!-- Process the selected nodes here -->
                <xsl:apply-templates select="$rootLevelNodes" />
            </ul>
        </xsl:template>
    
        <xsl:template match="*[@isDoc]">
            <!-- Select any children of this one -->
            <xsl:variable name="childNodes" select="*[@isDoc][not(umbracoNaviHide = 1)]" />
    
            <!-- Are we inside the "current" section? -->
            <xsl:variable name="inCurrentSection" select="ancestor-or-self::*[@id = $currentSection/@id]" />
    
            <!-- Are we in the "currentPage" branch? -->
            <xsl:variable name="inCurrentPageBranch" select="ancestor::*[@level &gt; $siteRoot/@level][@id = $currentPage/@id] or descendant-or-self::*[@id = $currentPage/@id]" />
    
            <li>
                <a href="{umb:NiceUrl(@id)}">
                    <xsl:value-of select="@nodeName" />
                </a>
    
                <!-- Should we show the children? -->
                <xsl:if test="$childNodes and ($inCurrentPageBranch or $inCurrentSection)">
                    <ul>
                        <xsl:apply-templates select="$childNodes" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Hope the additions make sense,

    /Chriztian

  • Tony Strack 16 posts 96 karma points
    Mar 19, 2018 @ 21:47
    Tony Strack
    0

    I owe you a beer...or beers. That code works great. Hit me up if you're ever in Minnesota, USA!

    Thank you VERY much for your help.

Please Sign in or register to post replies

Write your reply to:

Draft