How do I apply templates to each selected node in a for-each?

24,451

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:

  1. Add a name="track" attribute to the match="track" template, and then use <xsl:call-template/> from within the for-each (my idea, and worse than Tim C's)
  2. 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.
Share:
24,451
Jakob
Author by

Jakob

Updated on July 09, 2022

Comments

  • Jakob
    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 the match="track" template to each selected track element. If I've understood XSLT properly, with the current setup only child nodes of each selected track element are matched against templates, not the track 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
    ndim over 14 years
    Good catch on the sorting. I guess this is the best solution, due to the sorting.
  • Jakob
    Jakob over 14 years
    I could have sworn I already tried this, but I guess not. This is exactly what I was looking for, though!
  • Jakob
    Jakob over 14 years
    The 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
    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>.