Wrox Programmer Forums
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old October 27th, 2008, 09:05 AM
Authorized User
 
Join Date: May 2006
Posts: 30
Thanks: 0
Thanked 0 Times in 0 Posts
Default i=i+1

Hello,

My understanding is that you cannot re-assign values to variables in XSLT.

May I ask of how to approach the simple problem of i=i+1? The context is outputting so many table data cells and a certain number increment is reached you create a new table row until all records are output.

In ASP I would do this:

Code:
<table id="rates">
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
</tr>
<tr>
<%
TotalRecords = rsCurrencies2.RecordCount
EndOf = 0
Display = 2
NextLine = 0

Do While EndOf <> TotalRecords AND NOT rs.EOF
If rs.Fields.Item("ID").Value = 0 Then
Else
EndOf = EndOf + 1
If NextLine = Display Then
NextLine = 0 
Response.Write("</tr>")    
Response.Write("<tr>")    
End if    
%>

<td>
hello
</td>
<td>
hello again
</td>

<%
NextLine = NextLine + 1 

End If    
rs.MoveNext 
Loop

%>
So I have 2 variables that I would re-assign values to, 'NextLine' and 'EndOf'.

Many thanks for your help! I am not sure how to achieve the desired effect without adding 1 each time to a variable.





 
Old October 27th, 2008, 09:19 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Consider to post the XML input you have and the HTML output you want to create with your XSLT stylesheet.
Then tell us whether you want to use XSLT 1.0 or XSLT 2.0.
Then we can suggest how to solve that with XSLT.

--
  Martin Honnen
  Microsoft MVP - XML
 
Old October 27th, 2008, 09:39 AM
Authorized User
 
Join Date: May 2006
Posts: 30
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Hi Martin,

xml:
Code:
<root>
    <child>
        <name>John</name>
        <age>7</age>
        <****ize>5</****ize>
        <haircolour>Brown</haircolour>
    </child>
    <child>
        <name>Philip</name>
        <age>10</age>
        <****ize>7</****ize>
        <haircolour>Brown</haircolour>
    </child>
    <child>
        <name>Alexander</name>
        <age>5</age>
        <****ize>2</****ize>
        <haircolour>Blonde</haircolour>
    </child>
    <child>
        <name>Thomas</name>
        <age>2</age>
        <****ize></****ize>
        <haircolour>Brown</haircolour>
    </child>
</root>
html output (obviously there would be a table tag and headers but this is not within the loop)
Code:
<tr>
<td>John</td>
<td class="age">7</td>
<td>5</td>
<td style="font-weight: bold;">Brown</td>

<td>Philip</td>
<td class="age">10</td>
<td>7</td>
<td style="font-weight: bold;">Brown</td>
</tr>

<tr>
<td>Alexander</td>
<td class="age">5</td>
<td>2</td>
<td style="font-weight: bold;">Blonde</td>

<td>Thomas</td>
<td class="age">2</td>
<td></td>
<td style="font-weight: bold;">Brown</td>
</tr>
I am using version 1.0.

Testing in IE6&7 and FF

 
Old October 27th, 2008, 10:00 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

>how to approach the simple problem of i=i+1?

That isn't your problem, that's what your solution to the problem would be in a procedural language.

The simplest way of doing fixed-size groups in XSLT is probably:

<xsl:for-each select="row[position() mod $N = 1]">
  <tr>
   <xsl:for-each select=". | following-sibling::row[position() &lt; $N]">
     <td><xsl:value-of select="."/></td>
   </xsl:for-each>
  </tr>
</xsl:for-each>

The more general solution (needed for example if your criteria for starting a new group depend on the data you encounter) involves recursion, which is a technique you need to master to become a proficient XSLT programmer. Read all about it in your favourite XSLT textbook...

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
 
Old October 27th, 2008, 12:13 PM
Authorized User
 
Join Date: May 2006
Posts: 30
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Quote:
quote:Originally posted by mhkay
 >how to approach the simple problem of i=i+1?

That isn't your problem, that's what your solution to the problem would be in a procedural language.

The simplest way of doing fixed-size groups in XSLT is probably:

<xsl:for-each select="row[position() mod $N = 1]">
<tr>
<xsl:for-each select=". | following-sibling::row[position() &lt; $N]">
     <td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</xsl:for-each>

The more general solution (needed for example if your criteria for starting a new group depend on the data you encounter) involves recursion, which is a technique you need to master to become a proficient XSLT programmer. Read all about it in your favourite XSLT textbook...

Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer's Reference
Hi Michael,

Thank you for your time and effort but unfortnately your example is as clear as mud to me at this point.

Based on Martin's response I thought your example may pertain
to the example xml/xslt I provided. You'll have to excuse my ignorance, but I haven't the foggiest about what is going on in your code.

I don't have an XSLT book as I am not an XSLT coder normally. I work in ASP/PHP. I have inherited this project from someone else in our company who is on paternity at the moment.

I will google around your example below to find out what some of these 'functions?' mean. If you do have time to explain, I would very much appreciate it.


Code:
<xsl:for-each select="row[position() mod $N = 1]">
  <tr>
   <xsl:for-each select=". | following-sibling::row[position() &lt; $N]">
     <td><xsl:value-of select="."/></td>
   </xsl:for-each>
  </tr>
</xsl:for-each>
Many thanks!

 
Old October 27th, 2008, 12:32 PM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Assuming the XML input is
Code:
<root>
    <child>
        <name>John</name>
        <age>7</age>
        <size>5</size>
        <haircolour>Brown</haircolour>
    </child>
    <child>
        <name>Philip</name>
        <age>10</age>
        <size>7</size>
        <haircolour>Brown</haircolour>
    </child>
    <child>
        <name>Alexander</name>
        <age>5</age>
        <size>2</size>
        <haircolour>Blonde</haircolour>
    </child>
    <child>
        <name>Thomas</name>
        <age>2</age>
        <size></size>
        <haircolour>Brown</haircolour>
    </child>
</root>
then the following stylesheet
Code:
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:param name="items-per-row" select="2"/>

  <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
    <html lang="en">
      <head>
        <title>Example</title>
      </head>
      <body>
        <table>
          <tbody>
            <xsl:for-each select="root/child[position() mod $items-per-row = 1]">
              <tr>
                <xsl:for-each select=". | following-sibling::child[position() &lt; $items-per-row]">
                  <td>
                    <xsl:value-of select="name"/>
                  </td>
                  <td class="age">
                    <xsl:value-of select="age"/>
                  </td>
                  <td>
                    <xsl:value-of select="size"/>
                  </td>
                  <td style="font-weight: bold;">
                    <xsl:value-of select="haircolour"/>
                  </td>
                </xsl:for-each>
              </tr>
            </xsl:for-each>
          </tbody>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>
should do what you want, using the approach suggested earlier by Michael Kay.

--
  Martin Honnen
  Microsoft MVP - XML
 
Old October 27th, 2008, 12:36 PM
Authorized User
 
Join Date: May 2006
Posts: 30
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Like I said, you'll have to excuse my ignorance but the code below did not work and I don't understand it to work out what is wrong.

Could you explain what is happening please?

Code:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:param name="Domain"/>
<xsl:param name="remoteDomain"/>
<xsl:param name="UploadsFolder"/>

<xsl:template match="/">
     <div>

      <table>
        <tr>
        <td></td>
       <xsl:for-each select="row[position() mod $N = 1]">
          <tr>
           <xsl:for-each select=". | following-sibling::row[position() &lt; $N]">
             <td><xsl:value-of select="."/></td>
           </xsl:for-each>
          </tr>
        </xsl:for-each>

      </tr>
      </table> 

    </div>

 </xsl:template>
</xsl:stylesheet>
 
Old October 27th, 2008, 12:45 PM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

$N is supposed to be a variable you need to bind to a value representing the number of items you want to have in each row.
So you would need e.g.
Code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:param name="N" select="2"/>
And then look at the code I posted, it is written with XPath expressions matching the element names in your XML sample.

--
  Martin Honnen
  Microsoft MVP - XML
 
Old October 27th, 2008, 12:45 PM
Authorized User
 
Join Date: May 2006
Posts: 30
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Quote:
quote:Originally posted by trufla
 Like I said, you'll have to excuse my ignorance but the code below did not work and I don't understand it to work out what is wrong.

Could you explain what is happening please?

Code:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:param name="Domain"/>
<xsl:param name="remoteDomain"/>
<xsl:param name="UploadsFolder"/>

<xsl:template match="/">
     <div>

      <table>
        <tr>
        <td></td>
       <xsl:for-each select="row[position() mod $N = 1]">
          <tr>
           <xsl:for-each select=". | following-sibling::row[position() &lt; $N]">
             <td><xsl:value-of select="."/></td>
           </xsl:for-each>
          </tr>
        </xsl:for-each>

      </tr>
      </table> 

    </div>
     
 </xsl:template>
</xsl:stylesheet>
The problem I am having is seeing how the re-assignment is happening and how the grouping works.

Also, I must have something wrong because this is still not working after I looked over Martin's example:

Code:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:param name="Domain"/>
<xsl:param name="remoteDomain"/>
<xsl:param name="UploadsFolder"/>
<xsl:param name="items-per-row" select="2"/>


<xsl:template match="/">
     <div>
           <table>
          <tbody>
            <xsl:for-each select="currencies/currency[position() mod $items-per-row = 1]">
              <tr>
                <xsl:for-each select=". | following-sibling::currency[position() &lt; $items-per-row]">
                  <td>
                    <xsl:value-of select="currencyName"/>
                  </td>
                  <td class="age">
                    <xsl:value-of select="currencyName"/>
                  </td>
                  <td>
                    <xsl:value-of select="currencyName"/>
                  </td>
                  <td style="font-weight: bold;">
                    <xsl:value-of select="currencyName"/>
                  </td>
                </xsl:for-each>
              </tr>
            </xsl:for-each>
          </tbody>
        </table>
   </div>

 </xsl:template>
</xsl:stylesheet>
I will have a closer look

 
Old October 27th, 2008, 12:58 PM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Well it looks like you have changed your sample data.
Unless you show us the new XML sample data you are now trying to process it is not possible to see why your attempt failed.
The stylesheet I posted works with the sample data I posted (and that was only slightly adjusted to correct the <****ize></****ize> the forum software included in your original sample).
As for an explanation, the <xsl:for-each select="root/child[position() mod $items-per-row = 1]"> processes the first and third and fifth and so on 'child' element to produce a 'tr' HTML element in the output for each of tthese elements. Then the inner <xsl:for-each select=". | following-sibling::child[position() &lt; $items-per-row]"> processes that 'child' element and its sibling 'child' element to produce the 'td' HTML elements, meaning it processes the first and second 'child', then on the next run of the outer for-each the third and fourth and so on.



--
  Martin Honnen
  Microsoft MVP - XML









Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.