Could you please post the entire code? I have a feeling this could might be solved in a much better way but would like to see all you got first to make sure I get it all right.
You need to perform the same selection in the test, as you're using in the for-each - right now, your initial test checks if $currentPage has more than one parent node (including itself), which is true for all pages in the site, save for the root node. So copy the select expression from the for-each and use it in the test, which should work.
(You don't need to wrap the count() method around it - the test will return false if there are no nodes to select)
I can recommend taking a look at the apply-templates approach to doing XSLT - for navigations, sitemaps etc. it's so much easier to manage. Here's a rewrite of your XSLT for comparison:
<xsl:param name="currentPage" />
<!-- Customization -->
<xsl:variable name="startLevel" select="2" />
<xsl:variable name="lastLevel" select="4" />
<!-- Grab the root of the navigation -->
<xsl:variable name="navRoot" select="$currentPage/ancestor-or-self::*[@level = $startLevel]" />
<xsl:template match="/">
<ul class="leftNav">
<xsl:apply-templates select="$navRoot/*[@isDoc][sideNavShow = 1]" />
</ul>
</xsl:template>
<!-- Same template for all items in nav -->
<xsl:template match="*[@isDoc]">
<li>
<!-- If currentPage is somewhere below add an active(_child) class -->
<xsl:if test="descendant-or-self::*[@id = $currentPage/@id]">
<xsl:attribute name="class">
<xsl:text>active</xsl:text>
<xsl:if test="@level > $startLevel">_child</xsl:if>
</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
<!-- Continue processing children until (and including) $lastLevel -->
<xsl:if test="*[@isDoc][@level <= $lastLevel][sideNavShow = 1]">
<ul class="leftSubNav">
<xsl:attribute name="class">
<xsl:text>leftSub</xsl:text>
<xsl:if test="@level = $lastLevel">Sub</xsl:if>
<xsl:text>Nav</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="*[@isDoc][sideNavShow = 1]" />
</ul>
</xsl:if>
</li>
</xsl:template>
One thing that would make it even better would be to handle the multiple levels generically in CSS instead of adding different class names ('active' vs. 'active_child' and 'leftSubNav' vs. 'leftSubSubnav') on every level (but that's probably a little out of scope for this).
I am still pretty new to Umbraco (about 4 months in) and haven't yet gotten around to really learning XSLT. Thus far I've just been copy and pasting and poking around in the dark until I get something sort of right. I really need to learn it though, so this will definetly help get me started. This way of doing things looks much cleaner and easier to mantain. If you've got any tips for learning XSLT please share. I'm going to start with the OurUmbraco XSLT section.
no nodes leaves empty <ul>
I tried searching the forums and I found similar posts but nothing that worked exactly.
When I have no nodes in my XSLT I get a ul tag like this:
<div id="leftNavHolder">
<ul class="leftNav" />
</div>
I added a test to make sure there are child nodes on the current node before showing the list but I am still getting the empty ul tag.....any ideas?
heres the short version of my code:
<xsl:if test="count($currentPage/ancestor-or-self::*) > 1">
<ul class="leftNav">
<xsl:for-each select="$currentPage/ancestor-or-self::* [@level=$L3]/* [@isDoc and string(sideNavShow) = '1']">
<li></li>
</xsl:for-each>
<xsl:if test="count($currentPage/ancestor-or-self::*) > 1">
<ul class="leftNav">
<xsl:for-each select="$currentPage/ancestor-or-self::* [@level=$L3]/* [@isDoc and string(sideNavShow) = '1']">
<li>
</li>
</xsl:for-each>
</ul>
</xsl:if>
I thought it should check to make sure the count of child nodes is greater than 1.
How do I test that there are child nodes before spitting out the UL?
whooops, that first post got screwed up, look at the second post for the xslt
Hi Tim
You should probably make sure that it's a document you're dealing with.
Make your count like this count($currentPage/ancestor-or-self::*[@isDoc] > 0).
/Jan
Just saw your second post...
Could you please post the entire code? I have a feeling this could might be solved in a much better way but would like to see all you got first to make sure I get it all right.
/Jan
Hi Tim,
You need to perform the same selection in the test, as you're using in the for-each - right now, your initial test checks if $currentPage has more than one parent node (including itself), which is true for all pages in the site, save for the root node. So copy the select expression from the for-each and use it in the test, which should work.
(You don't need to wrap the count() method around it - the test will return false if there are no nodes to select)
/Chriztian
thank you Chriztian, that worked perfectly!
here is my final code for anyone with a similar issue.
<xsl:param name="currentPage"/>
<!-- Input the documenttype you want here -->
<xsl:variable name="L3" select="2"/>
<xsl:variable name="L4" select="3"/>
<xsl:variable name="TotalNodes" select="count(*)"/>
<xsl:template match="/">
<!-- The fun starts here -->
<!--l3 navigation-->
<xsl:if test="$currentPage/ancestor-or-self::* [@level=$L3]/* [@isDoc and string(sideNavShow) = '1']">
<ul class="leftNav">
<xsl:for-each select="$currentPage/ancestor-or-self::* [@level=$L3]/* [@isDoc and string(sideNavShow) = '1']">
<li>
<xsl:if test="$currentPage/ancestor-or-self::* [@level]/@id = current()/@id">
<xsl:attribute name="class">
<xsl:text>active</xsl:text>
</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
<!--l4 navigation-->
<xsl:if test="count(./* [@isDoc and string(sideNavShow) = '1']) > 0">
<ul class="leftSubNav">
<xsl:for-each select="./* [@isDoc and string(sideNavShow) = '1']">
<li>
<xsl:if test="$currentPage/ancestor-or-self::* [@level]/@id = current()/@id">
<xsl:attribute name="class">
<xsl:text>active_child</xsl:text>
</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
<!--l5 navigation-->
<xsl:if test="count(./* [@isDoc and string(sideNavShow) = '1']) > 0">
<ul class="leftSubSubNav">
<xsl:for-each select="./* [@isDoc and string(sideNavShow) = '1']">
<li>
<xsl:if test="$currentPage/ancestor-or-self::* [@level]/@id = current()/@id">
<xsl:attribute name="class">
<xsl:text>active_child</xsl:text>
</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
</li>
</xsl:for-each>
</ul>
</xsl:if>
<!-- end l5 -->
</li>
</xsl:for-each>
</ul>
</xsl:if>
<!-- end l4 -->
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
Hi Tim,
I can recommend taking a look at the apply-templates approach to doing XSLT - for navigations, sitemaps etc. it's so much easier to manage. Here's a rewrite of your XSLT for comparison:
One thing that would make it even better would be to handle the multiple levels generically in CSS instead of adding different class names ('active' vs. 'active_child' and 'leftSubNav' vs. 'leftSubSubnav') on every level (but that's probably a little out of scope for this).
/Chriztian
Thanks Chriztian,
I am still pretty new to Umbraco (about 4 months in) and haven't yet gotten around to really learning XSLT. Thus far I've just been copy and pasting and poking around in the dark until I get something sort of right. I really need to learn it though, so this will definetly help get me started. This way of doing things looks much cleaner and easier to mantain. If you've got any tips for learning XSLT please share. I'm going to start with the OurUmbraco XSLT section.
thanks again :)
is working on a reply...