Copied to clipboard

Flag this post as spam?

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


  • Garrett Fisher 341 posts 496 karma points
    Jan 08, 2010 @ 21:00
    Garrett Fisher
    0

    How to assign dynamic HTML tag attributes in an XSLT for-each

    Hi,

    In my main nav (using [XSLT] Navi), I am attempting to add attributes to my anchor tags in the for-each loop and ami getting VERY strange results, like a race condition with a previously called Macro which causes the HTML ouput to be "Mixed."  Here is what I am doing:

    <a href="{umbraco.library:NiceUrl(@id)}" id="nav_{@id}" title="{@nodeName}"></a>

    When I take these two attributes OUT, everything outputs fine.  Why can't I do this??  I also tried doing the following just above it in the loop:

    <xsl:attribute name="id">
    <xsl:value-of select="@id" />
    </xsl:attribute>

    But then the UI throws an error saying "An item of type 'Attribute' cannot be constructed within a node of type 'Root'."  I don't believe the loop is Ever AT the root, though, here is my for-each statement:

    <xsl:for-each select="$currentPage/ancestor-or-self::node/node [@level = 2 and string(data[@alias='umbracoNaviHide']) != '1']">

    What in the heck am I doing wrong here?  Why can't I just write the attributes while I'm writing the link?  How do I add the attributes?

    Thanks!!!

    //Garrett

  • Peter Dijksterhuis 1442 posts 1722 karma points
    Jan 08, 2010 @ 21:17
    Peter Dijksterhuis
    0

    Hi,

    if you want to write an attribute, make sure to place it between the opening and closing tag to which you want to apply it.

    <a href and more stuff here>

    <xsl:attribute name="id">
         <xsl:value-of select="@id" />
    </xsl:attribute>

    </a>

    HTH,

    Peter

  • Jesper Hauge 298 posts 487 karma points c-trib
    Jan 08, 2010 @ 21:46
    Jesper Hauge
    0

    Your first example should be perfectly valid (the one with the curly braces)

    I'm more concerned with your xpath in the for-each. As far as I can see the statement "$currentPage/ancestor-or-self::node" is equivalent to just "$currentPage". This is because ancestor-or-self::node will search for the first xml-node of type "node" it finds starting with $currentPage, and since all xml-nodes representing documents in umbraco-xml is of type "node" your statement will always find $currentPage immediately.

    I'm guessing you want to go to the current page ancestor that is on level 2 and start from there in which case your xpath should be

    <xsl:for-each select="$currentPage/ancestor-or-self::node[@level='2' and @umbracoNaviHide != '1'">

    Regards
    Jesper Hauge

  • Jesper Hauge 298 posts 487 karma points c-trib
    Jan 08, 2010 @ 21:51
    Jesper Hauge
    0

    Arghh - there were some errors in my previous post

    1. There was a syntax error a "]" is missing near the end.
    2. The ancestor-or-self::node xpath wil only select one node, so you will want to look for some nodes below the selected node appending /node[something] to the xpath.
    If this doesn't help, try posting some more code to look at.
    Regards
    .Hauge

  • Garrett Fisher 341 posts 496 karma points
    Jan 08, 2010 @ 22:37
    Garrett Fisher
    0

    Thanks Jesper-- I tried the curly braces again inline w/ he anchor tag, and I get empty output.  Here is the complete loop:

    <xsl:for-each select="$currentPage/ancestor-or-self::node/node [@level='2' and @umbracoNaviHide != '1']">

    <a href="{umbraco.library:NiceUrl(@id)}" id="nav_{@id}" title="nav_{@nodeName}"></a>

    </xsl:for-each>

    Same thing if I do this:

    <xsl:for-each select="$currentPage/ancestor-or-self::node/node [@level='2' and @umbracoNaviHide != '1']">

    <a href="{umbraco.library:NiceUrl(@id)}">

    <xsl:attribute name="id">
    <xsl:text>nav_</xsl:text>
    <xsl:value-of select="@id" />
    </xsl:attribute>

    <xsl:attribute name="title">
    <xsl:text>nav_</xsl:text>
    <xsl:value-of select="@nodeName" />
    </xsl:attribute>

    </a>

    </xsl:for-each>

    Still having a lot of trouble with this for some reason. 

    Thanks again,
    Garrett

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Jan 08, 2010 @ 22:51
    Chriztian Steinmeier
    0

    @Jesper: Using ancestor-or-self::node will in fact return a set consisting of all the node elements in the ancestor chain - not just the first. Adding the predicate [@level = 2] effectively filters the set, resulting in only one node.

    @Garrett: Thus, the select you posted will actually select all level 2 nodes that aren't hidden — that might become a problem if the rest of the code assumes a single element returned...

    — I'm with Jesper on the example with the curlies (or "attribute value templates" as they are called) - perfectly valid XSLT.

    We need more code :-)

    /Chriztian

  • Chriztian Steinmeier 2800 posts 8790 karma points MVP 8x admin c-trib
    Jan 08, 2010 @ 22:54
    Chriztian Steinmeier
    0

    Hi Garret,

    Regarding your last examples - you're now using @umbracoNaviHide instead of data[@alias = 'umbracoNaviHide'] which obviously won't work...

    /Chriztian

  • Jesper Hauge 298 posts 487 karma points c-trib
    Jan 09, 2010 @ 11:19
    Jesper Hauge
    0

    Hi again Garret

    Christian is right about the ancestor-or-self thing, it selects all the parents,  grandparents etc. of the current node. But I'm still not sure what your statement really selects, does it select all nodes below the node and its parent that is on level 2, or does it select all nodes below just one of the nodes in the ancestor tree. You could check that by inserting an

    <xsl:copy-of select="$currentPage/ancestor-or-self::node/node [@level='2' and @umbracoNaviHide != '1']" />

    This will emit the xml resulting from the xpath into your page output, so you can do a view source and check that the output is actually what you expect. If it is not, keep trying until you get it right.

    Here's what I would do:

    If you're trying to output all node at level="2" in the entire site the easiest xpath statement would probably be:

    <xsl:for-each select="$currentPage/ancestor::root/descendant::node[@level='2' and ./data[@alias='umbracoNaviHide'] != '1']">

    This is like saying go to the "root" node of the xml (the umbraco.config file always has a <root> node as the root), then select all descendants of type <node> that has attribute level='2' and a sub-xml-node <data alias="umbracoNaviHide"> with a value that is not "1".

    This might or might not work depending on your website structure. It only works if absolutely all nodes at level 2 are nodes that is supposed to be included in the navigation. For me this is not always the case since I will frequently have a datastructure with the site itself beneath a node with nodeTypeAlias='Website' and some other types of content (sidebars, footers etc.) in folders outside the website node, but these folder will also have content at level 2

    That's why I Usually have a main navigation xslt that looks like this:

    <xsl:for-each select="$currentPage/ancestor-or-self::node[@nodeTypeAlias='Website']/node[./data[@alias='umbracoNaviHide'] != '1']

    This tells the xlst parser to go to the ancestor (or self) that is of nodeTypeAlias "Website" and select all nodes beneath that which isn't hidden with umbracoNavihide

    Regards
    Jesper Hauge

  • Garrett Fisher 341 posts 496 karma points
    Jan 11, 2010 @ 23:22
    Garrett Fisher
    0

    Hi guys, thanks again for all the help.  Here is my complete code:

    <xsl:template match="/">

    <xsl:variable name="rootNode" select="$currentPage/ancestor-or-self::node [@level=1]" />

    <xsl:for-each select="$rootNode/node [@level='2' and data[@alias = 'umbracoNaviHide'] != '1']">

    <a href="{umbraco.library:NiceUrl(@id)}" id="nav_{@id}" title="{@nodeName}"></a>

    </xsl:for-each>

    </xsl:template>

    But it is Literally writing the last Level 2 node links SIX times.  There are definietly, positively no other Level 2 nodes after this last link, so I'm lost as to how this is happening.  The links are still being written even after I'm well into the HTML output fropm the NEXT Macro in the template.  For example:

    <div id="mainNav_holder">
    <!-- Navigation -->
    <a href="/about-gtp.aspx" id="nav_1081" title="About GTP"></a><a href="/property-owners.aspx" id="nav_1107" title="Property Owners"></a><a href="/site-locator.aspx" id="nav_1251" title="Site Locator"></a><a href="/site-solutions.aspx" id="nav_1109" title="Site Solutions"></a><a href="/team-gtp.aspx" id="nav_1110" title="Team GTP"></a><a href="/vendor-documents.aspx" id="nav_1235" title="Vendor Documents"></a>
    </div>

    <a href="/vendor-documents.aspx" id="nav_1235" title="Vendor Documents"></a>

    <div id="newsticker_holder">

    <a href="/vendor-documents.aspx" id="nav_1235" title="Vendor Documents"></a>
    <h2><a href="/vendor-documents.aspx" id="nav_1235" title="Vendor Documents">News</a></h2>
    <a href="/vendor-documents.aspx" id="nav_1235" title="Vendor Documents"></a>
    <div id="newsticker_content">
    <a href="/vendor-documents.aspx" id="nav_1235" title="Vendor Documents">
    <!-- Newsticker -->

    Strange, right?  What could be causing this??

    Thanks,

    Garrett

  • Jesper Hauge 298 posts 487 karma points c-trib
    Jan 11, 2010 @ 23:30
    Jesper Hauge
    0

    Your code looks good to me. If I was having this problem, I would stop looking at the navigation-xslt, and look at the code for the next macro. Perhaps try removing the following macro and see what happens.

    Could you post the code of the following macro here?

    Regards
    Jesper Hauge

  • Garrett Fisher 341 posts 496 karma points
    Jan 12, 2010 @ 17:45
    Garrett Fisher
    0

    I can't see how it would be the second Macro affecting it, as this last Main nav item repeats several times no matter what HTML occurs after it, but here is the code:

    <xsl:template match="/">

    <xsl:for-each select="$currentPage/descendant-or-self::node [@nodeTypeAlias = 'CWS_NewsItem' and string(data [@alias='umbracoNaviHide']) != '1']">

    <xsl:sort select="./data [@alias = 'date']" order="descending" />

    <xsl:if test="position() &lt;= $limit">

    <xsl:choose>
    <xsl:when test="@nodeTypeAlias = 'Offsite Redirect Page'">
    <a href="{umbraco.library:NiceUrl(@id)}" target="_new"><xsl:value-of select="umbraco.library:FormatDateTime(./data [@alias = 'date'], 'MM/dd/yy')"/> - <xsl:value-of select="@nodeName"/></a>
    </xsl:when>
    <xsl:otherwise>
    <a href="{umbraco.library:NiceUrl(@id)}"><xsl:value-of select="umbraco.library:FormatDateTime(./data [@alias = 'date'], 'MM/dd/yy')"/> - <xsl:value-of select="@nodeName"/></a>
    </xsl:otherwise>
    </xsl:choose>

    <xsl:if test="$includeAbstract = 1">

    <xsl:call-template name="FirstWords">
    <xsl:with-param name="words" select="25"/>
    <xsl:with-param name="text" select="umbraco.library:StripHtml(./data [@alias = 'text'])"/>
    </xsl:call-template>
    ...

    </xsl:if>

    </xsl:if>
    </xsl:for-each>
    </xsl:template>

    <xsl:template name="FirstWords">
    <xsl:param name="words"/>
    <xsl:param name="text"/>

    <xsl:if test="$words &gt;= 1">
    <xsl:if test="umbraco.library:LastIndexOf($text, ' ') &gt; 0">
    <xsl:value-of select="substring-before($text, ' ')" disable-output-escaping="yes"/><xsl:text> </xsl:text>
    </xsl:if>
    <xsl:if test="umbraco.library:LastIndexOf($text, ' ') &lt;= 0">
    <xsl:value-of select="$text" disable-output-escaping="yes"/>
    </xsl:if>
    <xsl:call-template name="FirstWords">
    <xsl:with-param name="words" select="$words - 1"/>
    <xsl:with-param name="text" select="substring-after($text, ' ')"/>
    </xsl:call-template>
    </xsl:if>
    </xsl:template>

     

  • Garrett Fisher 341 posts 496 karma points
    Jan 12, 2010 @ 18:48
    Garrett Fisher
    0

    All right, I figured this out.  Whew.

    This had nothing to do with how I was writing the <a> tag attributes or even my XSLT code.  This was because the main nav links are empty elements with background images.

    I am pretty sure this is an Umbraco bug: if there is no text in the <a> element, the transformation behaves erratically, as I said, writing the last node numerous times.  If I have ANY text in between the <a> and </a>, everything is fine.  So as a workaround, I'm using a "&nbsp;" there.

    The bug is that the <a> element cannot be empty.  This can easily be reproduced simply by creating an XSLT file using the "Navigation Prototype" selection from the dropdown, removing the <xsl:value-of select="@nodeName"/>, inserting the analogous Macro into a template, and then letting 'er rip in a browser.  Check out the HTML.

    Thanks so much for all your help with this, Jesper and Chriztian.

    //Garrett

  • Wyverex 7 posts 27 karma points
    Jan 28, 2010 @ 18:09
    Wyverex
    0

    I can confirm that you get strange results when you try to add empty tags in an XSLT transformation. As a workaround, you can set your XSLT's output method to "html" instead of "xml". Then it works, though I haven't figured out why yet. Could be a bug in Umbraco.

  • Jesper Hauge 298 posts 487 karma points c-trib
    Jan 28, 2010 @ 18:22
    Jesper Hauge
    0

    Hi Wyverex

    This isn't a bug in Umbraco: As Microsoft would put it, "this behaviour is by design", and it is a feature related to Microsofts xslt parser/transformation engine not to the code in Umbraco.

    When outputting xml self closing tags is allowed on any element, that is a tag looking like this <div /> is perfectly valid, a <div /> tag in (X)HTML on the other hand is something that makes most browsers choke, so whenever you're making a xslt-file where theres a risk that some tags comes out empty be sure to change the output method to html, which will render <div></div> and will always create self closing <img /> tags.

    This is because XHTML is a subset of xml, with some added constraints and standards. The output method "html" makes the xslt transformation adhere to the html standards when transforming xml.

    Regards
    Jesper Hauge

  • Wyverex 7 posts 27 karma points
    Jan 28, 2010 @ 18:31
    Wyverex
    0

    Good to know, thanks Jesper.

    At least there is an easy workaround.

  • Douglas Robar 3570 posts 4711 karma points MVP ∞ admin c-trib
    Jan 28, 2010 @ 21:43
    Douglas Robar
    0

    As Jesper says, this behavior is 'by design' in xslt when you're using the xml output method. Do be aware that the html output method may not be what you want either because it is html4 and not xhtml compliant. This can mess up your site's validation, for instance.

    You can read more on my blog at http://blog.percipientstudios.com/2009/4/11/anatomy-of-an-umbraco-xslt-file.aspx. Scroll down to the 'line 10' section that discusses the output method options and gottchas. It explains why xslt behaves the way it does as well as the pros and cons of the xml or html output methods.

    cheers,
    doug.

Please Sign in or register to post replies

Write your reply to:

Draft