How do I apply templates to each selected node in a for-each?
Solution 1
I would agree with 'ndim' that you should probably restructure your XSLT to do away with the xsl:for-each loop.
Alternatively, you could amend the xsl:apply-templates to select the current track node within the xsl:for-each
<xsl:for-each select="track">
<li>
<xsl:apply-templates select="." />
</li>
</xsl:for-each>
Keeping the xsl:for-each would, at least, allow you to sort the tracks in another order, if desired.
Solution 2
I'd restructure it a little (if you do not need the sorting the for-each
approach makes possible):
<xsl:template match="/album">
<ol>
<xsl:apply-templates select="track"/>
</ol>
</xsl:template>
<xsl:template match="track">
<li><a href="{@id}"><xsl:apply-templates/></a></li>
<xsl:template>
This looks shorter and more to the point, IMHO.
I guess your
<xsl:for-each select="track">
<li><xsl:apply-templates/></li>
</xsl:for-each>
walks through all track elements with the for-each
, and then applies the default rules to its descendants. So the content of the for-each
has the same context node as the match="track"
template has, and thus the match="track"
template never matches.
If you really want to use the for-each
in that way, you will need to change either of the following two things in your approach:
- Add a
name="track"
attribute to thematch="track"
template, and then use<xsl:call-template/>
from within thefor-each
(my idea, and worse thanTim C
's) - Use
Tim C
's solution using<xsl:apply-templates select="."/>
. This has the advantage of avoiding naming and keeping the possibility to sort the tracks.
Jakob
Updated on July 09, 2022Comments
-
Jakob almost 2 years
I know I'm missing something here. In the XSLT transformation below, the actual result doesn't match the desired result.
Inside the
for-each
, I want to apply thematch="track"
template to each selectedtrack
element. If I've understood XSLT properly, with the current setup only child nodes of each selectedtrack
element are matched against templates, not thetrack
elements themselves.How can I make the
track
elements go through the template as desired? Do I need to rethink my entire approach?Note: The transformation is executed using PHP. XML declarations have been omitted for brevity.
XML Document:
<album> <title>Grave Dancers Union</title> <track id="shove">Somebody To Shove</track> <track id="gold">Black Gold</track> <track id="train">Runaway Train</track> <producer>Michael Beinhorn</producer> </album>
XSL Stylesheet:
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/album"> <ol> <xsl:for-each select="track"> <li><xsl:apply-templates/></li> </xsl:for-each> </ol> </xsl:template> <xsl:template match="track"> <a href="{@id}"><xsl:apply-templates/></a> </xsl:template> </xsl:stylesheet>
Result:
<ol> <li>Somebody To Shove</li> <li>Black Gold</li> <li>Runaway Train</li> </ol>
Desired Result:
<ol> <li><a href="shove">Somebody To Shove</a></li> <li><a href="gold">Black Gold</a></li> <li><a href="train">Runaway Train</a></li> </ol>
-
ndim over 14 yearsGood catch on the sorting. I guess this is the best solution, due to the sorting.
-
Jakob over 14 yearsI could have sworn I already tried this, but I guess not. This is exactly what I was looking for, though!
-
Jakob over 14 yearsThe restructuring solution seems less modular than I would like (it doesn't allow me to also include tracks in a
<dl>
or<table>
somewhere else on the page/site, at least not using the same stylesheet), but you're probably right in that it's the smoothest solution for this example. -
janek37 almost 7 years@Jakob I am aware that this is ancient, but I just want to point out that you can use
mode
attribute to select another template for the same nodes. Like<xsl:apply-templates mode="table">
and<xsl:template match="track" mode="table">...</xsl:template>
.