Trying to understand why this XSLT is not working for me, and wondering if someone can help. On each of my pages, I have a custom property with alias of 'menuText', intended to allow the name of a menu item to be different from the nodeName. Currently, I have 3 pages at the same level which can populate a menu, therefore I would expect to see 3 items in the menu. The problem is this: while I do indeed see 3 items in the menu, the menuText for the first item in the menu is applied to all menu items. However, the link URLs in each case are correct.
At first glance, it looks like the problem is you are always referring to the same node to get the menuText - <xsl:value-of select="$parentElement/descendant::node/data[@alias='menuText']"/>
Not done yet.... Below is how the XSLT looks now. But, having added sub-level pages, they are not appearing in the menu as dropdowns (via RenderSubItems). I'm seeing a Javascript editor in the preview 'parent().get(...) is null or not an object'.
If I can be honest with you for a moment, the use of variables and XPath axes (ancestor / descendant) is way too confusing in your XSLT! Things can be much much simpler. Let me show you how.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="msxml umbraco.library">
<xsl:output encoding="utf-8" indent="yes" method="xml" omit-xml-declaration="yes" />
<xsl:param name="currentPage"/>
<xsl:template match="/">
<!-- set the variable with the parent node @level=2 -->
<xsl:variable name="parentNode" select="$currentPage/ancestor-or-self::node[@level=2]" />
<ul>
<!-- apply the templates to loop through each of the child nodes (that are not hidden from nav) -->
<xsl:apply-templates select="$parentNode/node[string(data[@alias='umbracoNaviHide']) != '1']">
<xsl:sort select="@nodeName" data-type="text" order="ascending" />
</xsl:apply-templates>
</ul>
</xsl:template>
<xsl:template match="node">
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<!-- check if the node has the 'menuText' set, otherwise fallback on the 'nodeName' -->
<xsl:choose>
<xsl:when test="string-length(data[@alias='menuText']) > 0">
<xsl:value-of select="data[@alias='menuText']" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@nodeName" />
</xsl:otherwise>
</xsl:choose>
</a>
<!-- check if the node has any children (that are not hidden from nav) -->
<xsl:if test="count(node[string(data[@alias='umbracoNaviHide']) != '1']) > 0">
<ul>
<!-- apply the templates for the sub-child nodes -->
<xsl:apply-templates select="node[string(data[@alias='umbracoNaviHide']) != '1']">
<xsl:sort select="@nodeName" data-type="text" order="ascending" />
</xsl:apply-templates>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
OK, in this XSLT we have 2 templates, the main one and another for <node> elements. For the main template I've kept the 'parentNode' variable, it's not really needed, but it's there just in case you need to do anything else with it. We then apply-templates against all the child <node> elements (that aren't hidden using 'umbracoNaviHide'...
For each <node> element that is matched, the template is applied... then within that we apply the template recursively to all/any child sub-pages.
Hope this makes sense? It should cut-down the amount of HTML you need to maintain too?
Lee, you're very kind to provide that sample and thank you. I understand how it works, and indeed it is simpler. However, I'm still not seeing the lower-level items, which are programmed to be visible. The Javascript error appears to be generated within the Dynamic Drive ddmenu.js. This works fine in my pure HTML version of the site I'm migrating, but not here. Maybe DD is not the way to go in this case... Aaagh! Is there an easier multi-level menu generator to use...?
OK, fixed it now. It was an incorrect ID on the menu <div>. Sorry for the confusion and many thanks again for your help. Now moving on to look at forms >> database. Oh, joy....
Staring at XSLT - no result...
Trying to understand why this XSLT is not working for me, and wondering if someone can help. On each of my pages, I have a custom property with alias of 'menuText', intended to allow the name of a menu item to be different from the nodeName. Currently, I have 3 pages at the same level which can populate a menu, therefore I would expect to see 3 items in the menu. The problem is this: while I do indeed see 3 items in the menu, the menuText for the first item in the menu is applied to all menu items. However, the link URLs in each case are correct.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:Stylesheet [ <!ENTITY nbsp " ">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:umbraco.library="urn:umbraco.library" exclude-result-prefixes="msxml umbraco.library">
<xsl:output omit-xml-declaration="yes" indent="yes" encoding="utf-8"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<xsl:variable name="pageId" select="$currentPage/@id"/>
<xsl:variable name="parentElement" select="$currentPage/ancestor-or-self::node[@level=2]"/>
<!--<h2><xsl:value-of select="$parentElement/@nodeName"/></h2>-->
<ul>
<xsl:for-each select="$parentElement//descendant::node[@level=3] [string(data[@alias='umbracoNaviHide']) != '1']">
<xsl:sort select="@nodeName" data-type="text" order="ascending" />
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="$parentElement/descendant::node/data[@alias='menuText']"/>
</a>
<xsl:call-template name="RenderSubItems">
<xsl:with-param name="parent" select="."/>
</xsl:call-template>
</li>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template name="RenderSubItems">
<xsl:param name="parent"/>
<xsl:if test="count($parent/descendant::node [string(data[@alias='umbracoNaviHide']) != '1']) > 0">
<ul>
<xsl:for-each select="$parent/descendant::node [string(data[@alias='umbracoNaviHide']) != '1']">
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="$parent/descendant::node/data[@alias='menuText']"/>
</a>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Thanks to anyone who can assist. I'm new to Umbraco, and although I understand its features, I'm finding it a bit of a pig to work with....
Hi Graham, welcome to the Umbraco community forum!
OK, the problem is with the reference to the node (that has the 'menuText' data), instead of this:
try removing the "$parentElement" stuff, so it's like this:
I know how frustrating XSLT/XPath can be... let us know how you get on!
Good luck, Lee.
Hi,
At first glance, it looks like the problem is you are always referring to the same node to get the menuText - <xsl:value-of select="$parentElement/descendant::node/data[@alias='menuText']"/>
This should be changed to
The '.' or 'current()' will give you the current item in the for loop, which sounds like what you want.
Thanks,
Tom
Brilliant! That worked a treat. Thanks very much guys for a quick reply. Issue closed...
Hi Graham, don't forget to mark the solution. :-) Thanks, Lee.
Not done yet.... Below is how the XSLT looks now. But, having added sub-level pages, they are not appearing in the menu as dropdowns (via RenderSubItems). I'm seeing a Javascript editor in the preview 'parent().get(...) is null or not an object'.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:Stylesheet [ <!ENTITY nbsp " ">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:umbraco.library="urn:umbraco.library" exclude-result-prefixes="msxml umbraco.library">
<xsl:output omit-xml-declaration="yes" indent="yes" encoding="utf-8"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<xsl:variable name="pageId" select="$currentPage/@id"/>
<xsl:variable name="parentElement" select="$currentPage/ancestor-or-self::node[@level=2]"/>
<ul>
<xsl:for-each select="$parentElement//descendant::node[@level=3] [string(data[@alias='umbracoNaviHide']) != '1']">
<xsl:sort select="@nodeName" data-type="text" order="ascending" />
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="./data[@alias='menuText']"/>
</a>
<xsl:call-template name="RenderSubItems">
<xsl:with-param name="parent" select="."/>
</xsl:call-template>
</li>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template name="RenderSubItems">
<xsl:param name="parent"/>
<xsl:if test="count($parent/descendant::node [string(data[@alias='umbracoNaviHide']) != '1']) > 0">
<ul>
<xsl:for-each select="$parent/descendant::node [string(data[@alias='umbracoNaviHide']) != '1']">
<xsl:sort select="@nodeName" data-type="text" order="ascending" />
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="./data[@alias='menuText']"/>
</a>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I feel such an idiot not getting this - I've used XSLT/XPATH in other apps for a long time, but in a different environment. Frustrating.
Hi Graham,
If I can be honest with you for a moment, the use of variables and XPath axes (ancestor / descendant) is way too confusing in your XSLT! Things can be much much simpler. Let me show you how.
OK, in this XSLT we have 2 templates, the main one and another for <node> elements. For the main template I've kept the 'parentNode' variable, it's not really needed, but it's there just in case you need to do anything else with it. We then apply-templates against all the child <node> elements (that aren't hidden using 'umbracoNaviHide'...
For each <node> element that is matched, the template is applied... then within that we apply the template recursively to all/any child sub-pages.
Hope this makes sense? It should cut-down the amount of HTML you need to maintain too?
Let us know how it goes.
Cheers, Lee.
Lee, you're very kind to provide that sample and thank you. I understand how it works, and indeed it is simpler. However, I'm still not seeing the lower-level items, which are programmed to be visible. The Javascript error appears to be generated within the Dynamic Drive ddmenu.js. This works fine in my pure HTML version of the site I'm migrating, but not here. Maybe DD is not the way to go in this case... Aaagh! Is there an easier multi-level menu generator to use...?
OK, fixed it now. It was an incorrect ID on the menu <div>. Sorry for the confusion and many thanks again for your help. Now moving on to look at forms >> database. Oh, joy....
No worries Graham, glad that you've got it working now.
Good luck with the rest of your site/project.
Cheers, Lee.
is working on a reply...