Search code examples
templatesxsltforeachmatchapply-templates

XSLT apply-templates inside for-each


I have an XSLT like below, and want to use apply-templates inside the xsl:for-each element so I don't have to repeat the <tr> element with the informations of the "cliente" XML element.

I'm trying but with no success to create a xsl:template and put xsl:apply-templates inside the xsl:for-each.

I know that I can use xsl:call-template, but is there any way to use xsl:apply-templates inside or outside the for-each?

Any idea on how to do this?

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/">
      <html>
         <head><title>Informações</title></head>
         <body>
            <h1>Relação de Clientes</h1>
            <table border="2">
               <tr bgcolor="LightBlue">
                  <th>Nome</th>
                  <th>Telefone</th>
                  <th>Cidade</th>
                  <th>Estado</th>
                  <th>Crédito</th>
               </tr>
               <tr>
                  <th colspan="6" bgcolor="LightPink">Critério usado abaixo: exibir todos os elementos ordenado por nome</th>
               </tr>
               <xsl:for-each select="informacoes/cliente">
               <xsl:sort select="nome" order="ascending" />
                  <tr>
                     <td bgcolor="LightGreen"><xsl:value-of  select="nome"/></td>
                     <td><xsl:value-of  select="telefone"/></td>
                     <td><xsl:value-of select="cidade"/></td>
                     <td><xsl:value-of select="estado"/></td>
                     <td><xsl:value-of select="credito"/></td>
                  </tr>
               </xsl:for-each>
               <tr>
                  <th colspan="6" bgcolor="LightCyan"> Critério usado abaixo: exibir os clientes da cidade do Rio de Janeiro</th>
               </tr>
               <xsl:for-each select="informacoes/cliente">
                   <xsl:if test="cidade='Rio de Janeiro'">
                      <tr>
                         <td bgcolor="LightGreen"><xsl:value-of  select="nome"/></td>
                         <td><xsl:value-of  select="telefone"/></td>
                         <td><xsl:value-of select="cidade"/></td>
                         <td><xsl:value-of select="estado"/></td>
                         <td><xsl:value-of select="credito"/></td>
                      </tr>
                    </xsl:if>
                </xsl:for-each>
               <tr>
                  <th colspan="6" bgcolor="LightYellow"> Critério usado abaixo:  exibir os clientes do estado do RJ com ordenado pelo nome; </th>
               </tr>
               <xsl:for-each select="informacoes/cliente">
               <xsl:sort select="nome" order="ascending" />
               <xsl:if test="estado='RJ'">
                  <tr>
                     <td bgcolor="LightGreen"><xsl:value-of  select="nome"/></td>
                     <td><xsl:value-of  select="telefone"/></td>
                     <td><xsl:value-of select="cidade"/></td>
                     <td><xsl:value-of select="estado"/></td>
                     <td><xsl:value-of select="credito"/></td>
                  </tr>
                </xsl:if>
                  </xsl:for-each>
               <tr>
                  <th colspan="6" bgcolor="LightYellow"> Critério usado abaixo:  exibir os clientes com crédito entre 250 e 400, em ordem descendente de crédito) </th>
               </tr>
               <xsl:for-each select="informacoes/cliente">
               <xsl:sort select="credito" order="descending" />
               <xsl:if test="credito&gt;250 and credito&lt;400">
                  <tr>
                     <td bgcolor="LightGreen"><xsl:value-of  select="nome"/></td>
                     <td><xsl:value-of  select="telefone"/></td>
                     <td><xsl:value-of select="cidade"/></td>
                     <td><xsl:value-of select="estado"/></td>
                     <td><xsl:value-of select="credito"/></td>
                  </tr>
                </xsl:if>
                  </xsl:for-each>
               </table>
            </body>
         </html>
      </xsl:template>
</xsl:stylesheet>

Solution

  • Inside of your xsl:for-each where you are iterating over informacoes/cliente, the context node will be the current cliente element.

    In order to apply-templates for the context node, you can use . in your select statement. For example:

    <xsl:for-each select="informacoes/cliente">
      <xsl:sort select="nome" order="ascending" />
      <xsl:apply-templates select="."/>
    </xsl:for-each>
    

    Then, create templates to match the cliente element:

    <xsl:template match="informacoes/cliente">
        <tr>
            <td bgcolor="LightGreen"><xsl:value-of  select="nome"/></td>
            <td><xsl:value-of  select="telefone"/></td>
            <td><xsl:value-of select="cidade"/></td>
            <td><xsl:value-of select="estado"/></td>
            <td><xsl:value-of select="credito"/></td>
        </tr>
    </xsl:template>
    

    You could also eliminate the <xsl:if> tests surrounding some of your items by referring to the current context node using the self:: axis and then applying the test criteria inside of a predicate filter on the context node:

      <xsl:for-each select="informacoes/cliente">
         <xsl:sort select="nome" order="ascending" />
         <xsl:apply-templates select="self::*[estado='RJ']"/>
      </xsl:for-each>
    

    Applying these changes to your example stylesheet:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="/">
            <html>
                <head><title>Informações</title></head>
                <body>
                    <h1>Relação de Clientes</h1>
                    <table border="2">
                        <tr bgcolor="LightBlue">
                            <th>Nome</th>
                            <th>Telefone</th>
                            <th>Cidade</th>
                            <th>Estado</th>
                            <th>Crédito</th>
                        </tr>
                        <tr>
                            <th colspan="6" bgcolor="LightPink">Critério usado abaixo: exibir todos os elementos ordenado por nome</th>
                        </tr>
                        <xsl:for-each select="informacoes/cliente">
                            <xsl:sort select="nome" order="ascending" />
                            <xsl:apply-templates select="."/>
                        </xsl:for-each>
                        <tr>
                            <th colspan="6" bgcolor="LightCyan"> Critério usado abaixo: exibir os clientes da cidade do Rio de Janeiro</th>
                        </tr>
                        <xsl:for-each select="informacoes/cliente">
                            <xsl:apply-templates select="self::*[cidade='Rio de Janeiro']"/>
                        </xsl:for-each>
                        <tr>
                            <th colspan="6" bgcolor="LightYellow"> Critério usado abaixo:  exibir os clientes do estado do RJ com ordenado pelo nome; </th>
                        </tr>
                        <xsl:for-each select="informacoes/cliente">
                            <xsl:sort select="nome" order="ascending" />
                            <xsl:apply-templates select="self::*[estado='RJ']"/>
                        </xsl:for-each>
                        <tr>
                            <th colspan="6" bgcolor="LightYellow"> Critério usado abaixo:  exibir os clientes com crédito entre 250 e 400, em ordem descendente de crédito) </th>
                        </tr>
                        <xsl:for-each select="informacoes/cliente">
                            <xsl:sort select="credito" order="descending" />
                            <xsl:apply-templates select="self::*[credito&gt;250 and credito&lt;400]"/>
                        </xsl:for-each>
                    </table>
                </body>
            </html>
        </xsl:template>
    
        <xsl:template match="informacoes/cliente">
            <tr>
                <td bgcolor="LightGreen"><xsl:value-of  select="nome"/></td>
                <td><xsl:value-of  select="telefone"/></td>
                <td><xsl:value-of select="cidade"/></td>
                <td><xsl:value-of select="estado"/></td>
                <td><xsl:value-of select="credito"/></td>
            </tr>
        </xsl:template>
    </xsl:stylesheet>   
    

    As demonstrated in Dimitre Novatchev's answer, you can further simplify your stylesheet by eliminating the xsl:for-each statements and adjusting your xsl:apply-templates select statements; applying an xsl:sort inside of the apply-templates where necessary to ensure the selected cliente elements are processed in the desired order.

    <xsl:apply-templates select="informacoes/cliente[estado='RJ']">
      <xsl:sort select="nome" order="ascending" />
    </xsl:apply-templates>