|Chapter 7 - Adding Programming Logic Isnt Just for Propheads|
|XSLT For Dummies|
|by Richard Wagner|
|Hungry Minds 2002|
The xsl:for-each element allows you to perform a set of instructions on each node returned from its select attribute. Its syntax is:
<xsl:for-each select="expression"> do something </xsl:for-each>
Essentially, the xsl:for-each element means that for each of the nodes returned by the select expression, perform the instructions in between the start and end tags.
Remember Im not trying to be a spoilsport, but like xsl:if and xsl:choose , you can use xsl:for-each only inside an xsl:template rule.
Technical Stuff xsl:for-each is a loose equivalent to the for statement found in other programming languages.
At this point, you may be asking yourself, Doesnt a template rule do the same thing as xsl:for-each ?" After all, a template rule processes the template instructions for each node returned from its match pattern. It is certainly true that template rules perform similar operations and are usually the best way to perform routines in XSLT. In fact, before automatically using xsl:for-each , see if you can do the same task with an ordinary template rule.
A common mistake many newcomers to XSLT make if theyve programmed in other languages is to overuse xsl:for-each rather than simply using template rules. After all, xsl:for-each looks much more familiar to a programmer than does xsl:template . However, XSLT asks you to think differently.
Ive found the xsl:for-each instruction works best when you need to loop through a set of nodes at the same time you are applying a template to another node.
To illustrate xsl:for-each in action, suppose I want to create a list of classes for each student in the Listing 7-1 document. I use the following stylesheet to accomplish this task:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <!-- Create list of classes for each student --> <xsl:template match="student"> <xsl:value-of select="@name"/>'s Classes: <xsl:for-each select="class"> <xsl:value-of select="@name"/> <xsl:if test="position()!=last()"> <xsl:text>, </xsl:text> </xsl:if> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet>
To compile a list for each student, a template rule is created to return the student element nodes. After using an xsl:value-of instruction to write the students name, I use xsl:for-each to loop through each class element and print the class name to the output document.
In the result document, Id like to separate each class name in the list with a comma, but if I just added <xsl:text>, <xsl:text> after the xsl:value-of instruction, Id get a trailing comma at the end of my list. As a result, I add xsl:text inside an xsl:if instruction that tests using the built-in XPath functions position() and last() to determine whether the node is the last one in the given node set. (See Chapter 11 for a complete discussion on built-in functions.)
The result document is as follows :
Jordan's Classes: Language Arts, Reading, Writing, Geography, Math, Science, History, Art Jared's Classes: Language Arts, Reading, Writing, Geography, Math, Science, History, Art Justus's Classes: Language Arts, Reading, Writing, Geography, Math, Science, History, Art
Technical Stuff For most practical purposes, you can think of xsl:for-each as looping through each node in the returned node set. However, to be completely accurate, xsl:for-each is technically not a loop, but a mapping. In other words, for each node encountered in the retuning node set, something is added to the output document.