Re: [xsl] Problems still with elements print first page only

Subject: Re: [xsl] Problems still with elements print first page only
From: tcn@xxxxxxxxxxxxx (Trevor Nash)
Date: Mon, 23 Jul 2001 10:23:40 GMT
Tamre,

>My problem is still that  the correct header information prints on page one
>of each statement e.g. acctnum and taxid, but on subsequent pages of the
>same statement this information does not print. I know I have a path problem
>as I go from
> [snip]

I will explain how to get your existing stylesheet working in a
moment, but first I should mention something else.

Taking a closer look at your stylesheet I saw:

><xsl:text disable-output-escaping="yes">
><![CDATA[</table></DIV>]]>
></xsl:text>

While this sort of trick can be made to work, you will find it gets
more complicated as new requirements are added.  I think there is also
a bug in your logic such that you will get an empty page if there are
an exact multiple of 8 transactions.

Your outline logic looks like:

    put header
    for each item
         put item
         if position mod 8 = 0
             put footer
             put header
         /if
    /for-each
    put footer

The problem with this is that as you have discovered you end up trying
to create start and end tags on their own.  XSLT is not designed to do
this.  While your trick of using disable-output-escaping can be made
to work, it is dangerous.  For example if you try to run your
stylesheet on the client, or in any other environment where the output
is not a file, you can find your tags being interpreted as text rather
than markup.

Instead try this:

     for-each item[position() mod 8 = 0
          put header
          for-each . | following-sibling::item[position()<8]
              put item
          /for-each
          put footer
     /for-each

Translation:
     for each item which is the first on a page
          put header
          put this item and up to 7 following (or, put
             all items on this page)
          put footer

As well as avoiding the need for the disable-output-escaping trick,
you will find your difficulty with empty pages also disappears.  The
only trouble with this aproach is that some XSLT processors will not
handle the following-sibling expression efficiently if there are a
large number of items.  

As David said, using apply-templates rather than for-each can be a
good idea - though to some degree it is a matter of taste.  You
probably do not want to replace *both* instances above.

Back to your existing stylesheet, and how to use parameters.  I am
doing this so that you can see how parameters work - once you get it
to work I suggest changing the logic to what I described above.  You
wil still need a parameter (or maybe just a variable) to keep track of
the current account, so the effort is not wasted.

><?xml version="1.0"?>
><xsl:stylesheet version="1.0"
>xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
>
><xsl:output method="html"/>
><xsl:template match="/">
><html>
><body>
><xsl:for-each select="statement/accountInfo">
>
We need to remember where we are here.  So create a variable:
   <xsl:variable name="accountRef" select="." />
'.' is a node-set, so we can use $accountRef on the front of any XPath
expression.

><xsl:call-template name="pagehead">
pagehead needs to be told which account to work with:
   <xsl:with-param name="account" select="$accountRef" />
I am deliberately using a different name for the variable and the
parameter so you do not get them mixed up.
></xsl:call-template>
>
><xsl:call-template name="pagetable">
and here too (because pagetable calls pagehead):
   <xsl:with-param name="account" select="$accountRef" />
></xsl:call-template>
>
></xsl:for-each> <!--statement/accountInfo-->
>
></body>
></html>
></xsl:template>
>
><xsl:template name="pagehead">
This template now takes a parameter.
    <xsl:param name="account"/>
The name matches the name in the with-param in the call-template.
><DIV style="position: relative; top:4px; width:4in;left:6in">
>
><xsl:value-of select="acctnum"/>
Its references like 'acctnum' which do not work.  Instead use
$account/acctnum.  Then, instead of using the current context (which
changes) you are using the template parameter (which is always right).
Similarly change the other relative paths.

>
> [snip]
></xsl:template>
>
><xsl:template name="pagetable">
This template also now takes a parameter.
    <xsl:param name="account"/>
><DIV>
><table id="table1">
>
><xsl:for-each select="transactions/transaction">
>
><xsl:variable name="rowsPerPage" select="8"/>
>
><xsl:call-template name="rowcount">
></xsl:call-template>
>
><xsl:if test="position() mod $rowsPerPage=0">
>
><xsl:text disable-output-escaping="yes">
><![CDATA[</table></DIV>]]>
></xsl:text>
>
>
><xsl:call-template name="pagehead">
pagehead needs to be told which account to work with:
   <xsl:with-param name="account" select="$account" />
This is a copy of the parameter pased to this template.
></xsl:call-template>
>
><xsl:text disable-output-escaping="yes">
><![CDATA[<DIV><table>]]>
></xsl:text>
></xsl:if>
>
></xsl:for-each> <!--transactions/transaction-->
>
></table>
></DIV>
></xsl:template>
>
>
><xsl:template name="rowcount">
>[snip]
></xsl:template>
>
></xsl:stylesheet>
>

Regards,
Trevor Nash
--
Traditional training & distance learning,
Consultancy by email

Melvaig Software Engineering Limited
voice:     +44 (0) 1445 771 271 
email:     tcn@xxxxxxxxxxxxx

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread