I want to select all nodes with a particular property value that reside below a particular node in my content heirachy. After reading around I've actually got it working, but think there must be a more concise way to do it in xpath:
<xsl:for-each select="$currentPage/ancestor-or-self::*[@isDoc and @level=1]/*[@isDoc and @nodeName='Resources']//*[@isDoc and contains(categories,$listingCategory)]"> <!-- output stuff --> </xsl:for-each>
In this case, the node with the name 'Resources' is what I want to start the search, then I want to filter on the 'categories' property of all its child nodes, selecting only where that contains the substring in $listingCategory.
Is there any more optimal way to do this? Seems like a lot of extra stuff in there.
The best way to narrow down an XPath is to make sure you include as much information you already know about the document you're selecting from. It can also be a good idea to create a couple of extra variables along the way. So let's go:
The first part grabs the root of the website (in an Umbraco install with multiple sites, the root of each is usually at level 1):
$currentPage/ancestor-or-self::*[@isDoc and @level = 1]
Here you can safely shave off the @isDoc predicate, because the ancestor axis of $currentPage can only contain document nodes.
The next part finds the 'Resources' node - here you can also get rid of the @isDoc predicate, because it's very unlikely (impossible in the current version) that an Umbraco property would ever have an attribute named @nodeName:
<!-- B) Special DocumentType: -->
<xsl:variable name="resources" select="$siteRoot/ResourcesFolder" />
So the last bit selects the documents you want to process, and since you're selecting based on the contents of a property, you probably also know which DocumentType that property sits on, right?
The "//" (descendant axis) here is very important to only include if needed, because it looks at ALL nodes that are descendants of the 'Resources' node.
That is, if you don't know if the documents can be children and/or grandchildren of the 'Resources' node you need it, but otherwise make sure to reduce it to what's actually needed. Indeed include the @isDoc predicate to at least only search Documents:
<!-- With a known DocumentType: -->
<xsl:variable name="documents" select="$resources//Presentation[contains(categories, $listingCategory)]" />
Again: If you know the documents needed will only be children (not grandchildren) of the 'Resources' node, you can save valuable processor time by using "/" in stead of "//".
Selecting specific nodes below a particular node
I want to select all nodes with a particular property value that reside below a particular node in my content heirachy. After reading around I've actually got it working, but think there must be a more concise way to do it in xpath:
In this case, the node with the name 'Resources' is what I want to start the search, then I want to filter on the 'categories' property of all its child nodes, selecting only where that contains the substring in $listingCategory.
Is there any more optimal way to do this? Seems like a lot of extra stuff in there.
Thanks!
- Andrew
Hi Andrew,
The best way to narrow down an XPath is to make sure you include as much information you already know about the document you're selecting from. It can also be a good idea to create a couple of extra variables along the way. So let's go:
The first part grabs the root of the website (in an Umbraco install with multiple sites, the root of each is usually at level 1):
Here you can safely shave off the @isDoc predicate, because the ancestor axis of $currentPage can only contain document nodes.
I usually put this in a variable called siteRoot:
If you have a special DocumentType that's only used for the website home (I always have) you can just use that, which will be the most efficient:
The next part finds the 'Resources' node - here you can also get rid of the @isDoc predicate, because it's very unlikely (impossible in the current version) that an Umbraco property would ever have an attribute named @nodeName:
Again - if you know the DocumentType, use that:
So the last bit selects the documents you want to process, and since you're selecting based on the contents of a property, you probably also know which DocumentType that property sits on, right?
The "//" (descendant axis) here is very important to only include if needed, because it looks at ALL nodes that are descendants of the 'Resources' node.
That is, if you don't know if the documents can be children and/or grandchildren of the 'Resources' node you need it, but otherwise make sure to reduce it to what's actually needed. Indeed include the @isDoc predicate to at least only search Documents:
Again: If you know the documents needed will only be children (not grandchildren) of the 'Resources' node, you can save valuable processor time by using "/" in stead of "//".
So final result:
<xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" /> <xsl:variable name="resources" select="$siteRoot/*[@nodeName = 'Resources']" /> <xsl:variable name="documents" select="$resources//*[@isDoc][contains(categories, $listingCategory)]" /> <xsl:for-each select="$documents"> <!-- output stuff --> </xsl:for-each>
Hope that gave you some insight (and an urge to become an XPath Ninja! :-)
/Chriztian
That's extraodinarily helpful, thanks so much!
4 months later and you still helping people Chriztian, Thanks a lot!
Hey Shaun,
Good to hear - thanks :-)
/Chriztian
Haha,
Give a ninja a shuriken he can assasinate a doge, teach a ninja to throw and he can join crouching tiger hidden dragon!
I love a good analogy >.<
is working on a reply...