I have a condition where I want my macros node selector to have the option of being parameter-less. How can it be done?
The parameter, "PrimaryCategory" filters on a property, like this:
<xsl:variable name="articleNodes" select="umbraco.library:GetXmlNodeById(1092)//story[contains(concat(',',primaryCategory,','),concat(',',$PrimaryCategory,',')) and archiveDate != '' and umbraco.library:DateGreaterThan($DateNow, displayDate) and @id != $currentPage/@id]" />
But sometimes, this macro will be used w/o a "Primary Category" like this:
<xsl:variable name="articleNodes" select="umbraco.library:GetXmlNodeById(1092)//story[archiveDate != '' and displayDate != '' and umbraco.library:DateGreaterThan(archiveDate, $DateNow) and umbraco.library:DateGreaterThan($DateNow, displayDate)]"/>
I want to do something like this:
<xsl:variable name="aritcleNodes">
<xsl:choose>
<xsl:when test="$PrimaryCategory == ''">
<xsl:value-of select="umbraco.library:GetXmlNodeById(1092)//story[archiveDate != '' and displayDate != '' and umbraco.library:DateGreaterThan(archiveDate, $DateNow) and umbraco.library:DateGreaterThan($DateNow, displayDate)]"" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="umbraco.library:GetXmlNodeById(1092)//story[contains(concat(',',primaryCategory,','),concat(',',$PrimaryCategory,',')) and archiveDate != '' and umbraco.library:DateGreaterThan($DateNow, displayDate) and @id != $currentPage/@id]" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
But I get:
To use a result tree fragment in a path expression, first convert it to a node-set using the msxsl:node-set() function.
Here's the trick to making that work - hopefully you can follow what's going on, otherwise feel free to ask about the specifics:
<!-- Set a single reference for the root of all articles; makes it easier to port the macro -->
<xsl:variable name="articleRoot" select="umbraco.library:GetXmlNodeById(1092)" />
<!-- Get all the <story> nodes that are not the current page -->
<xsl:variable name="validArticleNodes" select="$articleRoot//story[not(@id = $currentPage/@id)]" />
<!-- Get all nodes with $PrimaryCategory in their primaryCategory -->
<xsl:variable name="primaryCategoryNodes" select="
$validArticleNodes
[contains(concat(',', primaryCategory, ','), concat(',', $PrimaryCategory, ','))]
" />
<!-- Get a bool flag for whether there is $PrimaryCategory defined -->
<xsl:variable name="thereIsAPrimaryCategory" select="boolean(normalize-space($PrimaryCategory))" />
<!-- Set a variable for either all $primaryCategoryNodes (if one is specified) or all $validArticleNodes -->
<xsl:variable name="articleNodes" select="
$primaryCategoryNodes[$thereIsAPrimaryCategory]
| $validArticleNodes[not($thereIsAPrimaryCategory)]
" />
<!-- Iterate through all the selected nodes, filtered by their displayDate -->
<xsl:for-each select="$articleNodes[umbraco.library:DateGreaterThan($DateToday, displayDate)]">
<!-- Do something -->
</xsl:for-each>
Note that you can filter pretty much as much as you want, and that you can keep a very high level of readability just by adding a couple of intermediate variables.
Thanks Chriztian, this is a very interesting construct, basically $x[true]|$x[false]:
<!-- Set a variable for either all $primaryCategoryNodes (if one is specified) or all $validArticleNodes -->
<xsl:variable name="articleNodes" select="
$primaryCategoryNodes[$thereIsAPrimaryCategory]
| $validArticleNodes[not($thereIsAPrimaryCategory)]
" />
It all makes sense except for now I'm getting this error on $DateNow (you named it $DateToday above)
This seems like such an unlikely error because I haven't changed the format of the date from before, which
was just:
<xsl:variable name="articleNodes" select="umbraco.library:GetXmlNodeById(1092)//story[archiveDate != '' and displayDate != '' and umbraco.library:DateGreaterThan($DateNow, displayDate)]"/>
<!-- get the current time to compare to archive date -->
<xsl:variable name="DateNow" select="umbraco.library:CurrentDate()" />
<xsl:for-each select="$articleNodes[umbraco.library:DateGreaterThan($DateNow, displayDate)]" />
Error is
System.FormatException: String was not recognized as a valid DateTime.
at System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
at System.DateTime.Parse(String s)
at umbraco.library.DateGreaterThan(String firstDate, String secondDate)
I feel like I shouldn't be asking this question, but I'm looking at the DateGreaterThan and am baffled as to why it is now giving me an error! Do you see it? I'm not seeing it.
When you get that error it's almost always (99% guaranteed) because there's one or more nodes with either no date property or an empty date property ("date" meaning displayDate or archiveDate in your case).
When you do this:
<xsl:variable name="articleNodes" select="umbraco.library:GetXmlNodeById(1092)//story[archiveDate != '' and displayDate != '' and umbraco.library:DateGreaterThan($DateNow, displayDate)]"/>
- the DateGreaterThan() method will be run on all nodes - but if you do that in a separate predicate (the square brackets) it will only run if the previous predicate actually returned any nodes, e.g.:
<xsl:variable name="articleNodes" select="umbraco.library:GetXmlNodeById(1092)//story
[normalize-space(archiveDate) and normalize-space(displayDate)]
[umbraco.library:DateGreaterThan($DateNow, displayDate)]"
/>
(The normalize-space(), I've found, is the safest "check for not null")
I didn't know you could have multiple separate predicates like that (and I didn't know they were called "predicates").
I did a little test, and indeed, if I write:
<xsl:variable name="articleNodess" select="$articleNodes[displayDate != '' and archiveDate != '' and umbraco.library:DateGreaterThan($DateNow, displayDate)]" />
I do not get the error, but if I write:
<xsl:variable name="articleNodess" select="$articleNodes[umbraco.library:DateGreaterThan($DateNow, displayDate) and displayDate != '' and archiveDate != '' ]" />
I do get it. I guess it is evaluated from left to right.
It seems like it might be better in terms of performance then, to actually filter things out via separate predicates.
As, if I understand you correctly, the single predicate is doing extra, unnecessary work:
select="umbraco.library:GetXmlNodeById(1092)//story[archiveDate != '' and displayDate != '' and umbraco.library:DateGreaterThan($DateNow, displayDate)]
Actually, I am not sure what performance thresholds exist on running XSLT against "large" cache files. But what you have said makes total sense. I'm interested because I never met anyone with a cache file of > 100 mb. That site was choking, but a current production site has 20mb cache file and growing, so it's on my mind.
Thanks for the advice on normalize-space(); I will use that instead.
Anyways, thank you Jan and Chriztian for your comments. I really want to express my gratitude at how fast you guys respond and share your knowledge. It really makes my life easier. I was off the grid MX'ing, hence the late response. I respect y'alls time a lot.
variable node selector w/GetXmlNodeById
I have a condition where I want my macros node selector to have the option of being parameter-less. How can it be done? The parameter, "PrimaryCategory" filters on a property, like this:
But sometimes, this macro will be used w/o a "Primary Category" like this:
I want to do something like this:
But I get:
Hi Jacob
The above does not show how you try to render your variable but when you get a message like that you usually need to write it like this
Hope this helps.
/Jan
I'm just looping on articleNodes in a for loop:
If I just define the articleNodes variable without the choose it works.
Basically, the only way around this is to basically make a copy of this macro and change the one variable. Seems like a waste.
..Or maybe I'm just missing something.
Hi Jacob,
Here's the trick to making that work - hopefully you can follow what's going on, otherwise feel free to ask about the specifics:
Note that you can filter pretty much as much as you want, and that you can keep a very high level of readability just by adding a couple of intermediate variables.
/Chriztian
Thanks Chriztian, this is a very interesting construct, basically $x[true]|$x[false]:
It all makes sense except for now I'm getting this error on $DateNow (you named it $DateToday above)
This seems like such an unlikely error because I haven't changed the format of the date from before, which was just:
Error is
I feel like I shouldn't be asking this question, but I'm looking at the DateGreaterThan and am baffled as to why it is now giving me an error! Do you see it? I'm not seeing it.
Hi Jacob,
When you get that error it's almost always (99% guaranteed) because there's one or more nodes with either no date property or an empty date property ("date" meaning displayDate or archiveDate in your case).
When you do this:
- the
DateGreaterThan()
method will be run on all nodes - but if you do that in a separate predicate (the square brackets) it will only run if the previous predicate actually returned any nodes, e.g.:(The
normalize-space()
, I've found, is the safest "check for not null")/Chriztian
...I cherish these moments.
I didn't know you could have multiple separate predicates like that (and I didn't know they were called "predicates").
I did a little test, and indeed, if I write:
I do not get the error, but if I write:
I do get it. I guess it is evaluated from left to right.
It seems like it might be better in terms of performance then, to actually filter things out via separate predicates. As, if I understand you correctly, the single predicate is doing extra, unnecessary work:
Actually, I am not sure what performance thresholds exist on running XSLT against "large" cache files. But what you have said makes total sense. I'm interested because I never met anyone with a cache file of > 100 mb. That site was choking, but a current production site has 20mb cache file and growing, so it's on my mind.
Thanks for the advice on normalize-space(); I will use that instead.
Anyways, thank you Jan and Chriztian for your comments. I really want to express my gratitude at how fast you guys respond and share your knowledge. It really makes my life easier. I was off the grid MX'ing, hence the late response. I respect y'alls time a lot.
is working on a reply...