Use XSLT to format XML elements into HTML in sets of two
I have the following XML dataset (simplified), where I can have from zero to endless items (usually there will be 2-10):
<item template="event_element">
<mytitle>This is the first title</mytitle>
<mydate>20110330T143004</mytitle>
<mydescription>This is the first description</mytitle>
<mylink>www.example.com</mytitle>
</item>
.
.
<item template="event_element">
<mytitle>This is the Tenth title</mytitle>
<mydate>20110330T143004</mytitle>
<mydescription>This is the tenth description</mytitle>
<mylink>www.example.com</mytitle>
</item>
My end result should be like this, where I can end up with several sets of items (whic开发者_运维知识库h will be rotated by javascript):
<div class="body">
<div class="rel" id="arrangement">
// First set of items goes here
<div class="item">
<div class="itm">
<div class="in">
<p>
<a href="MY LINK" title="MY LINK DESCRIPTION">
<span>MY DATE</span>
<strong>MY FIRST TITLE</strong>
MY SHORT DESCRIPTION...
</a>
</p>
</div>
</div>
<div class="itm last">
<div class="in">
<p>
<a href="MY LINK" title="MY LINK DESCRIPTION">
<span>30. mar 2011</span>
<strong>MY SECOND TITLE</strong>
MY SHORT DESCRIPTION...
</a>
</p>
</div>
</div>
<div class="clr"></div>
</div>
// Second set of items goes here
<div class="item">
<div class="itm">
<div class="in">
<p>
<a href="MY LINK" title="MY LINK DESCRIPTION">
<span>MY DATE</span>
<strong>MY THIRD TITLE</strong>
MY SHORT DESCRIPTION...
</a>
</p>
</div>
</div>
<div class="itm last">
<div class="in">
<p>
<a href="MY LINK" title="MY LINK DESCRIPTION">
<span>30. mar 2011</span>
<strong>MY FORTH TITLE</strong>
MY SHORT DESCRIPTION...
</a>
</p>
</div>
</div>
<div class="clr"></div>
</div>
<div class="clr"></div>
</div>
My problem is stepping through my items (that should even be sorted by the date desc) and grouping them in sets of two.
As it is today I have been forced to hardcode the sets like this (the $eventfolder, $totalevents variables have been predefined):
<pre><code><div class="body">
<div class="rel" id="arrangement">
<xsl:if test="$totalevents > 0">
<div class="item">
<xsl:for-each select="$eventfolder/item[@template='event_element' and position() > 0 and position() < 3]">
<xsl:choose>
<xsl:when test="position() < $eventsperpage">
<div class="itm">
<xsl:call-template name="renderEvent" />
</div>
</xsl:when>
<xsl:otherwise>
<div class="itm last">
<xsl:call-template name="renderEvent" />
</div>
<div class="clr"></div>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</div>
</xsl:if>
<xsl:if test="$totalevents > 2">
<div class="item">
<xsl:for-each select="$eventfolder/item[@template='event_element' and position() > 2 and position() < 5]">
<xsl:choose>
<xsl:when test="position() < $eventsperpage">
<div class="itm">
<xsl:call-template name="renderEvent" />
</div>
</xsl:when>
<xsl:otherwise>
<div class="itm last">
<xsl:call-template name="renderEvent" />
</div>
<div class="clr"></div>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</div>
</xsl:if>
<div class="clr"></div>
</div>
</div>
</code></pre>
The renderElement template simply renders the inside event HTML.
But that way is not very practical if I wanted to show more sets than 2 - or even show more items in each set... The xslt file would be big and unreadable...
Any help to how I can solve this problem, since I cant figure out how to insert HTML tags as the XSLT compiler cant see them being closed - ie. (when test="position() = 1
insert html start tag and then later when test="position() = X
close the tag).
Thanks in advance
Without getting into the larger issues raised by your approach, in XSLT the way to group a list of elements n elements at a time is this:
<xsl:apply-templates select="*[(position()-1) mod $n = 0]" mode="group"/>
...which will apply the group-mode template to the first, n+1th, 2n+1th, etc. child elements of the current element. The group-mode template looks like this:
<xsl:template match="*" mode="group">
<group>
<xsl:apply-templates
select=".|following-sibling::*[position() < $n]"/>
</group>
</xsl:template>
This creates a group
element, and within that element, applies templates to the current element and its n-1 following siblings.
The net effect is that as long as n is a positive integer, the above will create group
elements containing n consecutive elements from the source tree. In your example, you'd create a div
instead of a group
, among other changes.
Regarding:
Any help to how I can solve this problem, since I cant figure out how to insert HTML tags as the XSLT compiler cant see them being closed - ie. (when
test="position() = 1
insert html start tag and then later whentest="position() = X
close the tag).
You're thinking about the problem wrong. XSLT generates trees, not tags. You create elements, not halves of elements.
The following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- the number of items to include in each group -->
<xsl:variable name="group" select="2" />
<xsl:template match="/">
<xsl:apply-templates
select="events/item[@template='event_element']
[(position() - 1) mod $group = 0]" />
</xsl:template>
<xsl:template match="item[@template='event_element']" mode="inner">
<div class="itm">
<div class="in">
<p>
<a href="{mylink}" title="{mydescription}">
<span><xsl:value-of select="mydate" /></span>
<strong><xsl:value-of select="mytitle" /></strong>
<xsl:value-of select="mydescription" />
</a>
</p>
</div>
</div>
</xsl:template>
<xsl:template match="item[@template='event_element']">
<div class="item">
<xsl:apply-templates
select=".|following-sibling::item[@template='event_element']
[position() < $group]"
mode="inner" />
<div class="clr" />
</div>
</xsl:template>
</xsl:stylesheet>
Applied to this input:
<events>
<item template="event_element">
<mytitle>This is the first title</mytitle>
<mydate>20110330T143004</mydate>
<mydescription>This is the first description</mydescription>
<mylink>www.example.com</mylink>
</item>
<item template="event_element">
<mytitle>This is the second title</mytitle>
<mydate>20110330T143004</mydate>
<mydescription>This is the tenth description</mydescription>
<mylink>www.example.com</mylink>
</item>
<item template="event_element">
<mytitle>This is the third title</mytitle>
<mydate>20110330T143004</mydate>
<mydescription>This is the tenth description</mydescription>
<mylink>www.example.com</mylink>
</item>
<item template="event_element">
<mytitle>This is the fourth title</mytitle>
<mydate>20110330T143004</mydate>
<mydescription>This is the tenth description</mydescription>
<mylink>www.example.com</mylink>
</item>
<item template="event_element">
<mytitle>This is the fifth title</mytitle>
<mydate>20110330T143004</mydate>
<mydescription>This is the tenth description</mydescription>
<mylink>www.example.com</mylink>
</item>
</events>
Produces the following output:
<div class="item">
<div class="itm">
<div class="in">
<p>
<a href="www.example.com" title="This is the first description">
<span>20110330T143004</span>
<strong>This is the first title</strong>
This is the first description
</a>
</p>
</div>
</div>
<div class="itm">
<div class="in">
<p>
<a href="www.example.com" title="This is the tenth description">
<span>20110330T143004</span>
<strong>This is the second title</strong>
This is the tenth description
</a>
</p>
</div>
</div>
<div class="clr" />
</div>
<div class="item">
<div class="itm">
<div class="in">
<p>
<a href="www.example.com" title="This is the tenth description">
<span>20110330T143004</span>
<strong>This is the third title</strong>
This is the tenth description
</a>
</p>
</div>
</div>
<div class="itm">
<div class="in">
<p>
<a href="www.example.com" title="This is the tenth description">
<span>20110330T143004</span>
<strong>This is the fourth title</strong>
This is the tenth description
</a>
</p>
</div>
</div>
<div class="clr" />
</div>
<div class="item">
<div class="itm">
<div class="in">
<p>
<a href="www.example.com" title="This is the tenth description">
<span>20110330T143004</span>
<strong>This is the fifth title</strong>
This is the tenth description
</a>
</p>
</div>
</div>
<div class="clr" />
</div>
Change the number of items to include in each group by modifying the value of the group
variable.
<xsl:template match="items">
<div>
<xsl:apply-template select="item"/>
</div>
</xsl:template>
<xsl:apply-templates match="item">
do stuff here
</xsl:apply-templates>
are you using something similar to this?
精彩评论