views:

36

answers:

3

I've an html table written using xslt transformation that looks like this

<table>
    <xsl:for-each select="someNode">
        <xsl:if test="testThis">
            <tr>
                <!-- <xsl:call-template name="conditionalRowStyle"/> -->
                <td>something</td>
            </tr>
         </xsl:if>
         <tr>
             <!-- <xsl:call-template name="conditionalRowStyle"/> -->
             <td>this is always displayed</td>
         </tr>
         <xsl:if test="testThis2">
            <tr>
                <!-- <xsl:call-template name="conditionalRowStyle"/> -->
                <td>something 2</td>
            </tr>
         </xsl:if>
         ....
    </xsl:for-each>
    <tr>
        <!-- <xsl:call-template name="conditionalRowStyle"/> -->
        <td>this is always displayed</td>
    </tr>
</table>

I need a way to apply different classes oddRow/evenRow to tr elems.

<tr class="evenRow"> or <tr class="oddRow">

I tried to use a template like this after every <tr> elem

<xsl:template name="conditionalRowStyle">
    <xsl:attribute name="class">
        <xsl:choose>
            <xsl:when test="(count(../preceding-sibling::tr) mod 2) = 0">oddrow</xsl:when>
            <xsl:otherwise>evenrow</xsl:otherwise>
        </xsl:choose>
    </xsl:attribute>
</xsl:template>

but this is not working. any idea?

+1  A: 

You could probably get away with doing this in just css

tr:nth-child(odd) {
    /*...*/
}
tr:nth-child(odd) {
    /*...*/
}

If you cannot, you could do something like

<xsl:attribute name="class">
    <xsl:choose>
        <xsl:when test="(position() mod 2) != 1">
            <xsl:text>evenRow</xsl:text></xsl:attribute>
        </xsl:when>
        <xsl:otherwise>
            <xsl:text>oddRow</xsl:text></xsl:attribute>
        </xsl:otherwise>
    <xsl:choose>
</xsl:attribute>

Note that i wrote this in the SO textbox and haven't tested it.

Kris
This will not work, since the selected nodes are the source data used to build the table, not the table itself.
mdma
Thanks for your resp. Your solutions won't work. I can't use css properties as in other tables I need to programmatically print 2+ rows of the same color. test="(position() mod 2) != 1 breaks when I use if statements to display conditional rows
mickthompson
A: 

It looks like the conditionalRowStyle template to add styles to the table is in the same stylesheet as the one building the table. If that's the case, then it will not work as expected, since the nodes selected in the conditionalRowStyle template will be from the source document (containing someNode) and not the target document (where the generated table elements are.)

You can "hack" this by collecting the table output of the someNode templates to a variable first, which you can then run the conditionalRowStyle template on first before finally outputting the variable value as the result of the stylesheet. But it's much simpler to use two stylesheets, that you run one after the other in a pipeline. The first stylesheet converts the someNode data to a table, and the second applies conditionalRowStyle formatting to the table.

mdma
+1  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vrtfTrs">
  <tr>
    <td>something</td>
  </tr>
  <tr>
    <td>This is always displayed</td>
  </tr>
  <tr>
    <td>something 2</td>
  </tr>
 </xsl:variable>

 <xsl:variable name="vTrs" select="ext:node-set($vrtfTrs)/*"/>

 <xsl:template match="/nums">
  <html>
    <table>
      <xsl:apply-templates select="num[1]"/>
    </table>
  </html>
 </xsl:template>

 <xsl:template match="num">
   <xsl:param name="pCount" select="0"/>

   <xsl:variable name="vTest1" select=". mod 4 = 1"/>
   <xsl:variable name="vTest2" select=". mod 4 = 2"/>

   <xsl:apply-templates select="$vTrs[1][$vTest1]">
     <xsl:with-param name="pCount" select="$pCount +1"/>
     <xsl:with-param name="pnodevalue" select="."/>
   </xsl:apply-templates>

   <xsl:apply-templates select="$vTrs[2]">
     <xsl:with-param name="pCount" select="$pCount+$vTest1 +1"/>
     <xsl:with-param name="pnodevalue" select="."/>
   </xsl:apply-templates>

   <xsl:apply-templates select="$vTrs[3][$vTest2]">
     <xsl:with-param name="pCount" select="$pCount+$vTest1 +2"/>
     <xsl:with-param name="pnodevalue" select="."/>
   </xsl:apply-templates>

   <xsl:apply-templates select="following-sibling::*[1]">
     <xsl:with-param name="pCount"
          select="$pCount+1+$vTest1+$vTest2"/>
     <xsl:with-param name="pnodevalue" select="."/>
   </xsl:apply-templates>

   <xsl:if test="not(following-sibling::*)">
       <xsl:apply-templates select="$vTrs[2]">
         <xsl:with-param name="pCount" select="$pCount+1+$vTest1+$vTest2"/>
         <xsl:with-param name="pnodevalue" select="."/>
       </xsl:apply-templates>
   </xsl:if>
 </xsl:template>

 <xsl:template match="tr">
  <xsl:param name="pCount"/>
  <xsl:param name="pnodevalue"/>

  <tr class="{concat(substring('even', 1 div ($pCount mod 2 = 0)),
                     substring('odd', 1 div ($pCount mod 2 = 1))
                     )}">
    <xsl:comment>&lt;num><xsl:value-of select="$pnodevalue"/>&lt;/num></xsl:comment>
    <xsl:copy-of select="node()"/>
  </tr>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

produces the wanted result:

<html>
   <table>
      <tr class="odd">
         <!--<num>01</num>-->
         <td>something</td>
      </tr>
      <tr class="even">
         <!--<num>01</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="odd">
         <!--<num>02</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="even">
         <!--<num>02</num>-->
         <td>something 2</td>
      </tr>
      <tr class="odd">
         <!--<num>03</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="even">
         <!--<num>04</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="odd">
         <!--<num>05</num>-->
         <td>something</td>
      </tr>
      <tr class="even">
         <!--<num>05</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="odd">
         <!--<num>06</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="even">
         <!--<num>06</num>-->
         <td>something 2</td>
      </tr>
      <tr class="odd">
         <!--<num>07</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="even">
         <!--<num>08</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="odd">
         <!--<num>09</num>-->
         <td>something</td>
      </tr>
      <tr class="even">
         <!--<num>09</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="odd">
         <!--<num>10</num>-->
         <td>This is always displayed</td>
      </tr>
      <tr class="even">
         <!--<num>10</num>-->
         <td>something 2</td>
      </tr>
      <tr class="even">
         <!--<num>10</num>-->
         <td>This is always displayed</td>
      </tr>
   </table>
</html>

Do note:

  1. We are using the most fine-grained traversal and processing of an XML document -- node by node. After the identity transformation this is the second most important XSLT design pattern.

  2. The rest of the small tricks are not that important.

Dimitre Novatchev
@Dimitre: +1 Excellent! Breaking the recursion and going sequentially with following axis...
Alejandro
@Alejandro: Yes, we need to remember that sometimes `<xsl:apply-templates/>` is too coarse.
Dimitre Novatchev