The event IDs are document IDs. Node that the last node contains an event with an ID already seen - this is a recurring event.
What I am doing is creating a list of all events for a date range, and then, this is the bit that is throwing a spanner in my later works, filtering them by various properties on the page:
<!-- Get all dates that have events -->
<xsl:for-each select="$pdcalendar/node[count(event) > 0]">
<!-- Get the date this event occurs on (important for recurretn events -->
<xsl:variable name="currentDate" select="@date"/>
<!-- Just do one display line per event - it is not broken down into groups of dates -->
<xsl:for-each select="event">
<xsl:variable name="eventDocument" select="umbraco.library:GetXmlNodeById(@id)"/>
<xsl:variable name="searchValue">
<!-- Concatentate some useful text fields from $eventDocument here -->
</xsl:variable>
<!-- Display events matching the filter conditions -->
<xsl:if test="$query_string_filter = '' or regex:test($searchValue, $query_string_filter, 'i')">
<xsl:call-template name="displayEvent">
<xsl:with-param name="currentDate" select="$currentDate"/>
<xsl:with-param name="event" select="$eventDocument"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
Now, this all works fine (any typos are from me simplifying for this post).
What the client wants now is a "Load more..." AJAX thing that gets blocks of events, so I want to generate all events for the year - easy, then get them in blocks of 5.
Unfortunately, because of the xsl:if filtering, I never have a node-set containing the final list of nodes to generate blocks using position(). And I can't simply add the filtering to the for-each (as far as I'm aware) because I need to go off and get the document nodes.
I also can't use the document nodes as the main working node-set because I need the dates returned by the calendar for recurrent events.
So, what is my next move? Is there a way to make this work? Or is there a completely different technique I need to use that I'm not seeing? :o)
Oh tits, that doesn't help that much either, as I need to get the combined position of the event within a date, whereas position() will just return the position of the current event WITHIN THE CURRENT DATE.
Hmm, okay, have a hacky answer. It's not very XSLT, so I'd love a "correct" way of doing this if there is one, but it's a lot more maintainable than sticking everything in the predicate.
It relies on the fact that the displayEvent template returns a single node/tag - in my case, an LI.
You can wrap both for-each loops, complete with filtering logic in an xsl:variable tag, then simply select out the LIs you want for the selected page from that, by using msxml:node-set() to make it iterable:
I don't really like it, but it's the best I've come up with so far and will enable me to go on holiday with a happy client :o)
In fact, you don't need the display template to return a single node - you can wrap in with a tag of your choosing and then just use the final select that does the paging to remove it before output.
Results filtering problem
I'm having conceptual issues with a filtering problem; the actual macro is huge so I'm going to try to post a cut down version.
I am using PDCalendar, which returns a nodeset similar to this:
The event IDs are document IDs. Node that the last node contains an event with an ID already seen - this is a recurring event.
What I am doing is creating a list of all events for a date range, and then, this is the bit that is throwing a spanner in my later works, filtering them by various properties on the page:
<!-- Get all dates that have events --> <xsl:for-each select="$pdcalendar/node[count(event) > 0]"> <!-- Get the date this event occurs on (important for recurretn events --> <xsl:variable name="currentDate" select="@date"/> <!-- Just do one display line per event - it is not broken down into groups of dates --> <xsl:for-each select="event"> <xsl:variable name="eventDocument" select="umbraco.library:GetXmlNodeById(@id)"/> <xsl:variable name="searchValue"> <!-- Concatentate some useful text fields from $eventDocument here --> </xsl:variable> <!-- Display events matching the filter conditions --> <xsl:if test="$query_string_filter = '' or regex:test($searchValue, $query_string_filter, 'i')"> <xsl:call-template name="displayEvent"> <xsl:with-param name="currentDate" select="$currentDate"/> <xsl:with-param name="event" select="$eventDocument"/> </xsl:call-template> </xsl:if> </xsl:for-each>
Now, this all works fine (any typos are from me simplifying for this post).
What the client wants now is a "Load more..." AJAX thing that gets blocks of events, so I want to generate all events for the year - easy, then get them in blocks of 5.
Unfortunately, because of the xsl:if filtering, I never have a node-set containing the final list of nodes to generate blocks using position(). And I can't simply add the filtering to the for-each (as far as I'm aware) because I need to go off and get the document nodes.
I also can't use the document nodes as the main working node-set because I need the dates returned by the calendar for recurrent events.
So, what is my next move? Is there a way to make this work? Or is there a completely different technique I need to use that I'm not seeing? :o)
Well, I have worked out that I can do this, at a pinch, in place of the: <xsl:for-eachselect="event">
<xsl:for-each select="event[($filter = '' or regex:test(concat(umbraco.library:GetXmlNodeById(@id)/@nodeName, ' ', umbraco.library:GetXmlNodeById(@id)/description), $filter, 'i'))]">
...but that's DISGUSTING, and will be even more so with more search fields, there HAS to be a better way than that.
Oh tits, that doesn't help that much either, as I need to get the combined position of the event within a date, whereas position() will just return the position of the current event WITHIN THE CURRENT DATE.
Please help, off on holiday soon :o)
Hmm, okay, have a hacky answer. It's not very XSLT, so I'd love a "correct" way of doing this if there is one, but it's a lot more maintainable than sticking everything in the predicate.
It relies on the fact that the displayEvent template returns a single node/tag - in my case, an LI.
You can wrap both for-each loops, complete with filtering logic in an xsl:variable tag, then simply select out the LIs you want for the selected page from that, by using msxml:node-set() to make it iterable:
<xsl:for-each select="msxml:node-set($final)/li[((position() - 1) >= $blockIndex * $blockSize) and ((position() - 1) < ($blockIndex + 1) * $blockSize)]">
<xsl:copy-of select="."/>
</xsl:for-each>
I don't really like it, but it's the best I've come up with so far and will enable me to go on holiday with a happy client :o)
In fact, you don't need the display template to return a single node - you can wrap in with a tag of your choosing and then just use the final select that does the paging to remove it before output.
is working on a reply...