Hello to all you Razor Synth Lords, wondering if you could help an old XSLT'er? ;-)
I got to a point in my current website where I needed to do a little templating, but felt a separate macro (whatever flavour) would be a tad overkill. So thought I'd give the new Inline Razor macro a try.
However I hit a small block... how do you do recursive fields?
i'm not sure if that would give the desired result... as AncestorsorSelf() returns a list of nodes, and not sure if that is in any particular order (as opposed to xslt which would traverse the tree upwards and find the first match). Might be wrong here, but giving it a shot anyway... but what if you added an extra OrderBy clause (for the Level property) and take the last (or first if using OrderByDescending)?
@Dirk I think the AncestorOrSelf() method returns a DynamicNodeList in order from the root down.
@Lee First() would give you the first node which matches the criteria starting with root and working down to the current node. Last() does the opposite so will take the last node (including itself) in the DynamicNodeList which matches the criteria.
Thanks Alex, that makes sense. In my case the "contactTelephone" property is only at the root/homepage node, so didn't matter which one I'd used (First()/Last()).
No problem Lee, still learning myself which is why I keep jumping on to the posts on here trying to absorb as much as possible, "learn by doing" and all that! :)
By the way just realised you can do this too, notice it's AncestorOrSelf instead of AncestorsOrSelf, so it will only return the first node which matches the criteria navigating up the tree.
I don't get this - why should AncestorOrSelf() with no arguments give you the root node? That doesn't make sense to me (but I'm heavily XPath-biased so...)
Is that just a convention, or how am I supposed to get that from the name?
Well, AncestorOrSelf, with no filtering parameters, would ultimately have to traverse all the way to the root, I guess? As there is no matching expression to stop the traversing. In that light, I guess it makes kind of sence?
Looking in reflector, we can make sure though, that we actually get the root node.
If this release of Umbraco supported extension methods on DynamicNode, you could wrap the AncestorOrSelf() method inside Root() or something, to make the method name make more sence :)
The following snippet works fine as long as the property type is set to e.g. textstring, but it doesn't return anything at all if the type is changed to Richtext editor. Am I missing something obvious here or is this a bug?
Just came across this thread and thought I'd supply a belated guess as to why Bjørn's query (above) doesn't work.
The Rich Text Editor's underlying type is of System.Web.HtmlString therefore you need to call ToString() on it to get the actual content to check if it is not empty. So you're query would be something like:
@Tom this was introduced in 4.7.1, I suspect you're using 4.7.0 then. You can still do: Model.GetProperty("yourPropertyAlias").Value (it has the same effect).
Just noticed what appears to be a weird bug in Razor recursive properties:
Basically I have a property on every page called headerImage. This is populated via a media picker and stores the id of the selected image. Not all pages have an image selected, so for some it will be empty. The root (home) node definitely has a value.
I then wrote some razor code to recursively find the last ancestor node that had a headerImage with a value:
Here is a very convenient way to do what you are asking, using special "leading underscore" on the property name. So, to answer the original question on page 1 of this thread (asked almost a year ago), we have :
<xsl:value-of select="$currentPage/ancestor-or-self::*[@isDoc and normalize-space(contactTelephone)][1]/contactTelephone"/>
... And in Razor, at least in version 4.7.1 (not sure if also works in older versions), you can simply do this:
@Model._contactTelephone
The leading underscore is the shortcut for recursive. I know this is an old topic but, for posterity, thought I would add this in case it helps someone else...
If I write an If/Else and the a statement is true will is search all the way up the tree until is sees the true statement?
Something like this
if (@Model._backgroundRepeat)
{
<text>repeat</text>
}
else if (@Model._backgroundRepeatVertical)
{
<text>repeat-y</text>
}
else if (@Model._backgroundRepeatHorizontal)
{
<text>repeat-x</text>
}
else
{
<text>no-repeat</text>
}
Will it look up the tree until it finds the true statement if not then hit the else? Because right now I have a sub page that is 3 levels deep. The page has the same property that the parent nodes have the same property. I want it set up so each page can have its own property option and if the option is not true on the current page, I want it to crawl up the tree until it finds the true one. Will that work?
Don't suppose anyone's worked out how to do this for a datatype like a multinode picker or embedded content where there is xml recorded, so always a value?
Recursive fields using Razor macro
Hello to all you Razor Synth Lords, wondering if you could help an old XSLT'er? ;-)
I got to a point in my current website where I needed to do a little templating, but felt a separate macro (whatever flavour) would be a tad overkill. So thought I'd give the new Inline Razor macro a try.
However I hit a small block... how do you do recursive fields?
Old school examples... <umbraco:Item>
XSLT
... now how to do this using Razor? Without going crazy complicated in C# syntax!
At the moment, I'm making use of the Macro Parameters, like so...
Could that be the better way of doing this? Very curious!
Thanks, Lee.
There is probably a much more elegant solution than this, but it should at least do what you want it to do.
@Model.AncestorsOrSelf().Where("contactTelephone != \"\"").Last().contactTelephone
Thanks Alex. I'm still getting my head around Razor syntax... so it's essentially Linq methods?
Slight tweak of yours, as I didn't like the string escaping... also do you think that using First() would make any difference?
Cheers, Lee.
PS. I'm probably going to look back on the reply in a few months and laugh at myself.
Lee,
i'm not sure if that would give the desired result... as AncestorsorSelf() returns a list of nodes, and not sure if that is in any particular order (as opposed to xslt which would traverse the tree upwards and find the first match). Might be wrong here, but giving it a shot anyway... but what if you added an extra OrderBy clause (for the Level property) and take the last (or first if using OrderByDescending)?
Let me know what you find out...
Cheers,
/Dirk
@Dirk I think the AncestorOrSelf() method returns a DynamicNodeList in order from the root down.
@Lee First() would give you the first node which matches the criteria starting with root and working down to the current node. Last() does the opposite so will take the last node (including itself) in the DynamicNodeList which matches the criteria.
Thanks Alex, that makes sense. In my case the "contactTelephone" property is only at the root/homepage node, so didn't matter which one I'd used (First()/Last()).
All good for my learning curve!
Cheers, Lee.
No problem Lee, still learning myself which is why I keep jumping on to the posts on here trying to absorb as much as possible, "learn by doing" and all that! :)
By the way just realised you can do this too, notice it's AncestorOrSelf instead of AncestorsOrSelf, so it will only return the first node which matches the criteria navigating up the tree.
wow, this is really cool Alex, and thanks for sharing
Cheers,
/Dirk
Lee, if all you want is the root node of your site, AncestorOrSelf() without any parameters, will give you just that:
@Model.AncestorOrSelf().contactTelephone
Excellent, thanks Mads, good to know!
I don't get this - why should AncestorOrSelf() with no arguments give you the root node? That doesn't make sense to me (but I'm heavily XPath-biased so...)
Is that just a convention, or how am I supposed to get that from the name?
/Chriztian
Well, AncestorOrSelf, with no filtering parameters, would ultimately have to traverse all the way to the root, I guess? As there is no matching expression to stop the traversing. In that light, I guess it makes kind of sence?
Looking in reflector, we can make sure though, that we actually get the root node.
If this release of Umbraco supported extension methods on DynamicNode, you could wrap the AncestorOrSelf() method inside Root() or something, to make the method name make more sence :)
So much to learn.. :)
He Alex, just tested your expression:
but it gives me a:
Operator '!=' cannot be applied to operands of type 'umbraco.MacroEngines.DynamicNull' and 'string'
error when the property is empty. This one works though:
@Model.AncestorsOrSelf().Where("contactTelephone != \"\"").Last().contactTelephone
Looks like our recursive queries are going to get a little easier in future versions:
Added GetPropertyValue(string alias) and GetPropertyValue(string alias, bool recursive) to DynamicNode
Five weeks on, and I'm wondering: did the tantalising new methods in the change set above actually get implemented?
Cos when I try and use them, e.g
var courses=Model.GetProperty("availabilityChecker", true);
I get:
Error loading Razor Script FSCListofRecommendedCourses.cshtml
Cannot invoke a non-delegate type
If I'm just being dumb, please someone do let me know. Cos the methods above for getting recursive properties would be really really useful.
There haven't been a new Umbraco release since the above change set, so, they are quite surely implemented, just not officially released :)
The following snippet works fine as long as the property type is set to e.g. textstring, but it doesn't return anything at all if the type is changed to Richtext editor. Am I missing something obvious here or is this a bug?
Code:
Just came across this thread and thought I'd supply a belated guess as to why Bjørn's query (above) doesn't work.
The Rich Text Editor's underlying type is of System.Web.HtmlString therefore you need to call ToString() on it to get the actual content to check if it is not empty. So you're query would be something like:
GetPropertyValue isn't working for me at all...
Hi Tom,
What version of Umbraco are you using? and what are you trying to get?
@Tom this was introduced in 4.7.1, I suspect you're using 4.7.0 then. You can still do: Model.GetProperty("yourPropertyAlias").Value (it has the same effect).
Just noticed what appears to be a weird bug in Razor recursive properties:
Basically I have a property on every page called headerImage. This is populated via a media picker and stores the id of the selected image. Not all pages have an image selected, so for some it will be empty. The root (home) node definitely has a value.
I then wrote some razor code to recursively find the last ancestor node that had a headerImage with a value:
However, the above always ended up returning NULL and wouldn't work (even though I'd used similar code before).
However, a standard recursive page field did work fine and returned the correct id:
In the end I found I had to do the following in Razor which did work:
I've no idea why. I suspect it might be because the media picker stores the value as an int rather than a string?
Anyone any clues?
Hi Dan,
Did you try with .Where("headerImage != null")?
Cheers,
Michael.
Michael - I thought I must have, but when I checked I actually hadn't since it worked fine. Feel a bit stupid now :)
Thanks for the tip - it works great now!
Great!
Glad I could help. Sometimes we are so deep in our own stuff that we don't see/think of the obvious ;-)
Cheers,
Michael.
I'm having a problem with getting a recursive value with .GetPropertyValue.
I created a codeplex item for it. Please vote : http://umbraco.codeplex.com/workitem/30607
Here is a very convenient way to do what you are asking, using special "leading underscore" on the property name. So, to answer the original question on page 1 of this thread (asked almost a year ago), we have :
Old school examples... <umbraco:Item>
XSLT
... And in Razor, at least in version 4.7.1 (not sure if also works in older versions), you can simply do this:
The leading underscore is the shortcut for recursive. I know this is an old topic but, for posterity, thought I would add this in case it helps someone else...
Love it! that is fantastic! Thanks Funka! wish it was documented!
@Tom It's documented here :) http://our.umbraco.org/projects/developer-tools/razor-dynamicnode-cheat-sheet
Jeroen
How far up the tree does the _recursive go. Right now I see it only going up 1 level.
Think it should go up all the way until it's found somewhere.
Jeroen
If I write an If/Else and the a statement is true will is search all the way up the tree until is sees the true statement?
Something like this
if (@Model._backgroundRepeat)
{
<text>repeat</text>
}
else if (@Model._backgroundRepeatVertical)
{
<text>repeat-y</text>
}
else if (@Model._backgroundRepeatHorizontal)
{
<text>repeat-x</text>
}
else
{
<text>no-repeat</text>
}
Will it look up the tree until it finds the true statement if not then hit the else? Because right now I have a sub page that is 3 levels deep. The page has the same property that the parent nodes have the same property. I want it set up so each page can have its own property option and if the option is not true on the current page, I want it to crawl up the tree until it finds the true one. Will that work?
I am on 4.7.1
Nevermind. Got recursive to work. in my case I has to use AncestorOrSelf.
Don't suppose anyone's worked out how to do this for a datatype like a multinode picker or embedded content where there is xml recorded, so always a value?
Or something
Even with all these great responses, I am struggling with Razor recursion...
Here is a simplified snippet of my macro:
Assuming I have a content structure like this:
HOME => (doctype 'Website') has RL property named "MainNavLinks"
- Products => (doctype Textpage) does not have RL property named "MainNavLinks"
And I have a template which is used on both pages which includes this:
This works propery on the HOME page = the list of nav links is output.
But it fails on the Products page = "MatchingNodeId" is equal to the current page's Id (Products) and "CountTest" is equal to 2.
I have tried these variations with the same result:
and
What am I missing? Why do these expressions return "true" for the current page which doesn't even have the property?
Thanks!
Hi,
I also found that the "Related Links" and the "uComponents Multi-URL Picker" data types were problematic to implement recursivley.
As "Tony Kiernan" posted, "...there is xml recorded, so always a value" even when nothing has been selected.
As a result I was able to implement recursion for the "Related Links" data type the following way:
Can anyone suggest a better alternative?
is working on a reply...