Our Forum has Moved

This site is our old forum and is only here for achive until we get proper 301 redirects setup to make Google happy.

Please use our new community site - Our Umbraco - which contains an improved forum, documentation wiki, package repository and a member locator.

Go to Our Umbraco now

Learn everything about Umbraco
XSLT Tip - Display a field recursively... Options
neehouse
Posted: Friday, December 22, 2006 7:50:00 PM

Rank: Umbracoholic

Joined: 7/20/2006
Posts: 1,150
Location: Charleston, West Virginia, United States
A while back, a couple of people were asking how to display a field in XSLT in a recursive manner. I recently had to call a field in a recursive manner, with a bit of logic, and thought I would share my knowledge.

Simple Recursion

To select a single field in a recursive manner is actually quite simple.

Code:

<xsl:value-of select="$currentPage/ancestor-or-self::node [string(data[@alias='field'])!=''] [position()=last()] /data[@alias='field']"/>


The trick behind this is that for each node returned with a value on the field requested, that the last one (closest parent node), is the one we want to retrieve. Thus, the last position.

Of course, if you are rotating through a set of nodes with a for-each call, you can replace the $currentPage with . or current().

Conditional Recursion

If you have a more logic based situation (simple conditions), such that one field dictates the display of the other, you can add your logic into the xslt select, expanding the Simple Recursion with additional clauses.

For instance, I have two fields that determine the display of an object on a page. One field, 'hideField', is a yes/no data type, and the other, 'Field', is a textbox multiple.

I want to display my 'Field' recursively, but only if the 'hideField' has not been set to yes (1) along the way. For that, I have to extend the selection a bit. This could also be accomplished with an if statement, but I think this manner is a bit cleaner for this example.

Code:

<xsl:value-of select="$currentPage/ancestor-or-self::node [string(data[@alias='Field'])!='' or data[@alias='hideField']=1] [position()=last()] [data[@alias='hideField']!=1] /data[@alias='Field']"/>


What this is basically doing is finding the last node (closest parent) that either has content in the Field or the hideField option is set to yes(1). If that last item does not have hideField set to yes(1)[aka, it is set to no], then the Field is displayed.

Fallback Recursion

If you want to display a secondary field if the primary is not available, but maintain the recursive approach, some fun coding must occur. It isn't as pretty, but, it gets the job done.

Code:

<xsl:value-of select="$currentPage/ancestor-or-self::node [string(data[@alias='Primary'])!='' or string(data[@alias='Secondary'])!=''] [position()=last()] [data[@alias='Primary']!=''] /data[@alias='Primary']"/>

<xsl:value-of select="$currentPage/ancestor-or-self::node [string(data[@alias='Primary'])!='' or string(data[@alias='Secondary'])!=''] [position()=last()] [data[@alias='Secondary']!='' and data[@alias='Primary']=''] /data[@alias='Secondary']"/>


What this xslt statement is doing is Looking at the Primary and Secondary fields on the node tree. If either one has a value, the node is selected, from which we take the last node (closest parent). We then test for data in the Primary field, which if it contains data, we are shown that field. The second statement does the same as the first, however, it tests for data on the Secondary field, but only if there is no data on the Primary field, as we don't want to show both fields.

Additional Thoughts

If you are trying to read content from a field that uses the Richtext Editor, you may have to apply the umbraco.library:StripHtml function into your code, as to keep you from selecting a field that has a set of paragraph tags, but no content.

While these solutions may not be an exact answer to your needs, I hope it opens up the possibilities that XSLT can do for you.

As a side note, I did not test every example here (specifically the last example), but did test the first and second examples, barring any typing errors.

Case



• 2007/2008 MVP • 2008/2009 MVP • Certified •
Best Case Technologies, LLC
• Umbraco Licensing • Support Packages • Web and Application Development • Hosting • Multi-Lingual Site •
mortenbock
Posted: Wednesday, December 27, 2006 1:10:07 PM

Rank: Addict

Joined: 7/19/2006
Posts: 882
Location: Århus, Denmark
Nice how-to article :-)

thanks

Morten Bock - Level 2 certified - MVP 2008/2009 - My danish blog with a few english posts

neehouse
Posted: Wednesday, December 27, 2006 2:51:25 PM

Rank: Umbracoholic

Joined: 7/20/2006
Posts: 1,150
Location: Charleston, West Virginia, United States
Thank you Morton...

As time permits, and topics come available, I will try to write some additional Tips..

• 2007/2008 MVP • 2008/2009 MVP • Certified •
Best Case Technologies, LLC
• Umbraco Licensing • Support Packages • Web and Application Development • Hosting • Multi-Lingual Site •
neehouse
Posted: Monday, April 30, 2007 3:32:31 AM

Rank: Umbracoholic

Joined: 7/20/2006
Posts: 1,150
Location: Charleston, West Virginia, United States
As pointed out in another post, I made a mistake in the above code. instead of [position()=last()], in should read [position()=1] or just [1].

This is due to the way that XSLT actually orders entries when using the axis ancestor-or-self (or just ancestor for that matter). When using this axis, the results are returned with the items closest to the context node listed first.

So, if you used the above anywhere, please check and correct your code.

Casey

• 2007/2008 MVP • 2008/2009 MVP • Certified •
Best Case Technologies, LLC
• Umbraco Licensing • Support Packages • Web and Application Development • Hosting • Multi-Lingual Site •
bnkrazy
Posted: Thursday, July 17, 2008 3:08:23 PM
Rank: Enthusiast

Joined: 5/13/2008
Posts: 41
Location: VA, USA
Just wanted to say this tip was very useful! Both in terms of solving my problem and helping me learn a bit more about how xslt works. Thanks!

Chris
amritanshu
Posted: Tuesday, July 22, 2008 4:17:18 PM

Rank: Enthusiast

Joined: 7/2/2008
Posts: 34
Location: India
Thank you Morton... Its brilliant... thanks a ton...


Good judgment comes from experience, and often experience comes from bad judgment. So experience is simply the name we give our mistakes...
Silverbug
Posted: Wednesday, October 22, 2008 4:36:21 AM

Rank: Devotee

Joined: 12/16/2007
Posts: 65
Location: Auckland, NZ
Casey your a life saver! worked perfectly. :)

Paul
Sniper Systems Ltd
HFloyd
Posted: Sunday, December 28, 2008 10:44:29 PM

Rank: Fanatic

Joined: 7/19/2006
Posts: 226
Location: New York, NY, USA
Hmm...

I was able to get this to work for simple properties (like textstring) but not for a more complex datatype (namely the new RelatedLinks datatype)

If the site structure is like this:

Home (includes RelatedLinks property called "SiteRecommendedLinks")
-Page1
-Page2

And I want to show the SiteRecommendedLinks list, if I use just
Code:
<xsl:for-each select="$currentPage/data [@alias = $propertyAlias]/links/link">

in the XSLT, the list shows up on the Homepage, but not on any subpages (Page 1, Page 2)

So, in order to get the link list to appear on all the pages, I thought recursion would be the answer, but this doesn't return anything on any page (incl. 'Home'):
Code:

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


Any idea why this doesn't work?

Thanks, Heather




Whole Web Impact | Floyd Innovations LLC | Heather Floyd's Blog
Dirk
Posted: Monday, December 29, 2008 11:13:23 AM

Rank: Umbracoholic

Joined: 9/27/2007
Posts: 1,874
Location: Belgium
Hi Heather,

I'm guessing that it doesn't return anything as the value for the node property is again xml.
Have done some similar requests on a local install and the below code snippet:

Code:
<xsl:for-each select="$currentPage/ancestor-or-self::node">
<xsl:value-of select="@id"/>
<xsl:text>*</xsl:text><xsl:copy-of select="string(data [@alias='relatedLinks']/links/link[0])" /><xsl:text>*</xsl:text>
<xsl:if test="data [@alias='relatedLinks'] != ''">
<xsl:text>Ok</xsl:text>
</xsl:if>
<xsl:if test="count(data [@alias='relatedLinks']/links/link) &gt; 0">
<xsl:text>Ok, found one!</xsl:text>
</xsl:if>
</xsl:for-each>


(I've used 'relatedLinks' instead of your 'SiteRecommendedLinks')

does never return the 'Ok' string for any page in the hierarchy, even though the top level node has some related links.

I'd suggest to take another approach because the related links are only listed at the top level node, right? In that case, use

Code:
<xsl:for-each select="$currentPage/ancestor-or-self::node [@level = 1]">


to get to the top level node and list all links from there.

Hope this helps.

Regards,
/Dirk


level 1 & 2 certified - umbraco MVP 2008/2009 - umbraco blog at netaddicts.be - working on an integrated forum4umbraco
HFloyd
Posted: Tuesday, December 30, 2008 5:50:40 PM

Rank: Fanatic

Joined: 7/19/2006
Posts: 226
Location: New York, NY, USA
Thanks for your reply, Dirk,

That is what I ended up doing after all for my current application. I had been hoping to create a more flexible XSLT script for future use, though. Oh well.

Happy New Year!

Heather

Whole Web Impact | Floyd Innovations LLC | Heather Floyd's Blog

The forum has moved

This forum is no longer in use, so you can't reply to this message - please go to Our 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.