Solved

adding page numbers to xslt pagination

Posted on 2011-09-20
33
255 Views
Last Modified: 2012-05-12
Hi Gertone,

Adding on from the pagination we just did, how easy is it to add page numbers to the buttons?
So <prev 1,2,3,4.... next>

Thanks
0
Comment
Question by:help-is-needed
  • 17
  • 16
33 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36566033
<xsl:value-of select="floor($first div $size)"/>
will likely give you the number you need
0
 

Author Comment

by:help-is-needed
ID: 36566183
Hi,

Thanks. Sorry that gives me the current page number, but how do I make is so that it displays these as links?

....

<< first <prev 1 2 3 4 5... next> last>>

Thanks
0
 

Author Comment

by:help-is-needed
ID: 36567274
Please see my XSLT and XML example:

I am having some issues with the styling also. When I created the look of a record in HTML it looks fine, now that I am displaying the results using XML and XSLT it doesn't seem to be closing many of the divs so the style is very bizarre and a lot of the inline styles end up getting applied to following divs because the div hasn't been closed. I can't figure out why though.

XML:
 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
 <Product Page_Nr="41">    
  <Story>
   <ServiceName>Test Name</ServiceName>
   <Product_Rating>5</Product_Rating>
   <Desc_Destinations>Test Desc</Desc_Destinations>
  </Story>
  <Story>
   <Desc_Snapshot>Blah blah blah</Desc_Snapshot>
   <Desc_Hotel_Facilities>test facilities</Desc_Hotel_Facilities>
   <Desc_Guest_Rooms>92 Rooms • Balcony • Fully equipped kitchen • Air-con • TV • Hairdryer • Iron and ironing board</Desc_Guest_Rooms>
   <Price>£118</Price>
   <Desc_Price_description>Superior Suite (Room Only)</Desc_Price_description>
  </Story>
  <Image_1 href="hotel-image.jpg"></Image_1>
 </Product>
</Root>

Open in new window


XSLT:
 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="1.0">
  <xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
  <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:param name="first">1</xsl:param>
  <xsl:param name="size">10</xsl:param>

  <xsl:template match="Root">
    <div>
      <xsl:call-template name="navigation"/>
      <xsl:apply-templates select="Product[position() >= $first and position() &lt; $first + $size]"/>
      <xsl:call-template name="navigation"/>
    </div>
  </xsl:template>

  <xsl:template match="Product">
    <!-- do product stuff here -->
    <xsl:if test="Story/ServiceName != '' and Story/Desc_Destinations != '' and Story/Product_Rating != ''">
      <div id="TeaserBase" style="background-color: #000000; color:#ffffff; width:960px; font: 81%/1.4 Arial,Verdana,sans-serif;">
        <div id="teaser_content" style="padding:10px;">
          <div id="teaser_image" style="float:left; padding:10px 5px 10px 10px">
            <xsl:element name="img">
              <xsl:attribute name="border">0</xsl:attribute>
              <xsl:attribute name="src">
                <xsl:value-of select="Image_1/@href"/>
              </xsl:attribute>
              <xsl:attribute name="width">430</xsl:attribute>
              <xsl:attribute name="height">325</xsl:attribute>
              <xsl:attribute name="alt">
                <xsl:value-of select="Story/ServiceName"/>
              </xsl:attribute>
            </xsl:element>
          </div>
          <div id="teaser_right" style="float:left; padding:10px 5px 0 10px">
            <div style="font-size:18px; font-weight:bold; max-width:480px">
              <xsl:value-of select="translate(Story/ServiceName, $lowercase, $uppercase)"/>
            </div>
            <div>
              <xsl:choose>
                <xsl:when test="Story/Product_Rating = 1">
                  <img src="Star.png" alt="1" title="1" ></img>
                </xsl:when>
                <xsl:when test="Story/Product_Rating = 2">
                  <img src="Star2.png" alt="2" title="2" ></img>
                </xsl:when>
                <xsl:when test="Story/Product_Rating = 3">
                  <img src="Star3.png" alt="3" title="3" ></img>
                </xsl:when>
                <xsl:when test="Story/Product_Rating = 4">
                  <img src="Star4.png" alt="4" title="4" ></img>
                </xsl:when>
                <xsl:when test="Story/Product_Rating = 5">
                  <img src="Star5.png" alt="5" title="5" ></img>
                </xsl:when>
              </xsl:choose>
              <span style="font-size:15px; font-weight:bold">
                <xsl:value-of select="translate(Story/Desc_Destinations, $lowercase, $uppercase)"></xsl:value-of>
              </span>
            </div>
            <div id="teaser_text" style="float:left; width:290px; max-height:290px; height:290px">
              <div style="width:280px; height:280px; overflow:hidden">
                <span style="font-size:12px;">
                  <xsl:value-of select="Story/Desc_Snapshot"/>
                </span>
              </div>
            </div>
            <div id="teaser_extra" style="float:left; padding-left:10px; width:180px; height:290px;">
              <div style="font-weight:bold">Hotel Facilities</div>
              <div style="max-height:70px; overflow:hidden">
                <xsl:value-of select="Story/Desc_Hotel_Facilities"/>
              </div>
              <div style="font-weight:bold; margin-top:5px;">Guest Rooms</div>
              <div style="max-height:70px; overflow:hidden">
                <xsl:value-of select="Story/Desc_Guest_Rooms"/>
              </div>
              <div style="font-weight:bold; margin-top:5px; font-size:14px">
                From
                <xsl:value-of select="Story/Price" /> per person per night
              </div>
              <div style="font-size:smaller">
                <xsl:value-of select="Story/Desc_Price_description"/>
              </div>
              <table style="padding-top:10px;">
                <tr>
                  <td>
                    <img src="btw_btn_email_enquiry.gif" />
                  </td>
                  <td>
                    <img src="btw_btn_find_branch.gif" />
                  </td>
                </tr>
              </table>
            </div>
          </div>
          <div style="clear:both"></div>
        </div>
      </div>
    </xsl:if>
  </xsl:template>

  <xsl:template name="navigation">
    <div>
      <a href="#">
        <xsl:attribute name="onclick">
          <xsl:text>showPage(</xsl:text>
          <xsl:value-of select="$first - $size"/>
          <xsl:text>,</xsl:text>
          <xsl:value-of select="$size"/>
          <xsl:text>);</xsl:text>
        </xsl:attribute>
        <xsl:text>previous</xsl:text>
      </a>
      <xsl:text>   </xsl:text>
      <a href="#">
        <xsl:attribute name="onclick">
          <xsl:text>showPage(</xsl:text>
          <xsl:value-of select="$first + $size"/>
          <xsl:text>,</xsl:text>
          <xsl:value-of select="$size"/>
          <xsl:text>);</xsl:text>
        </xsl:attribute>
        <xsl:text>next</xsl:text>
      </a>
      <xsl:text>   Page </xsl:text>
      <xsl:value-of select="floor($first div $size + 1)"/>
    </div>
  </xsl:template>

</xsl:stylesheet>

Open in new window

0
Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
ID: 36567330
Here is how I would do the navigation in that case
<xsl:template name="navigation">
        <div>
            <xsl:for-each select="Product[position() mod $size = 1]">
                <xsl:variable name="pos" select="position()"/>
                <xsl:variable name="this-start" select="1 + ($pos - 1) * $size"/>
                <xsl:choose>
                    <xsl:when test="$this-start &lt;= $first and $first &lt; $this-start + $size">
                        <span><xsl:value-of select="$pos"/></span>
                    </xsl:when>
                    <xsl:otherwise>
                        <a href="_blank">
                            <xsl:attribute name="onclick">
                                <xsl:text>showPage(</xsl:text>
                                <xsl:value-of select="$this-start"/>
                                <xsl:text>,</xsl:text>
                                <xsl:value-of select="$size"/>
                                <xsl:text>)</xsl:text>
                            </xsl:attribute>
                            <xsl:value-of select="$pos"/>
                        </a>
                    </xsl:otherwise>
                </xsl:choose>
                <xsl:text>   </xsl:text>
            </xsl:for-each>
        </div>
    </xsl:template>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36567338
well, I did not see you last post prior to posting, was working on it in the mean time,
will check your question now
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36567990
about styling... there are a number of <div> that I added to the equation in order to make the XSLT result well formed
That is because Sarissa needs that
You need to take that into account when you layout your pages

when I see this
    <xsl:if test="Story/ServiceName != '' and Story/Desc_Destinations != '' and Story/Product_Rating != ''">
I realize that you are breaking some of the pages because you are dropping Records

I would strongly advice to make the function do two XSLTs in a row
- one that takes the original sources and removes all the unwanted Records from the source (potentally sorting the remainder if needed)
- a second that does the visualisation (per page) starting from the result of the first step
Sarissa allows you easily to do that

This way the total number of Records per page still match

Other than the "float:left" I don't see anything risky that could break your layout
0
 

Author Comment

by:help-is-needed
ID: 36568026
That makes sense to remove the unwanted records, then work out the pagination.

How would I go about doing that?

Thanks for you help.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36568320
I added an Identity Transform with a simple extra rule for removing the Records you don't want
If you do this as a first step, you can use the result of this transform as the input of the second step
(both source and result are Sarissa Dom objects)

Now you can remove the if test from your 2nd XSLT
and size will always be correct
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Product[not(normalize-space(Story/ServiceName)) or not(normalize-space(Story/Desc_Destinations)) or not(normalize-space(Story/Product_Rating))]"/>
 </xsl:stylesheet>

Open in new window

0
 

Author Comment

by:help-is-needed
ID: 36568415
Sorry, you must think I am just being lazy or stupid but how do I apply this to my already code?
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36568444
can you show me the sarissa javascript code you allready have?
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36568447
might take a while, preparing dinner in the mean time :-)
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36568731
From the top of my head, untested, your javascript should look something like this

xslt1.xsl is the XSLT that does the identity transform (filtering the empty Records) from comment http://a#36568320
<head>
	<script type="text/javascript" src="sarissa.js" ></script>
	<script type="text/javascript" language="JavaScript">
      var oXmlDoc;
        oXmlDoc = Sarissa.getDomDocument();
        oXmlDoc.async = false;
        oXmlDoc.load("source.xml");

      var oXslStep1;
        oXslStep1 = Sarissa.getDomDocument();
        oXslStep1.async = false;
        oXslStep1.load("xslt1.xsl");

      var oXslStep2;
        oXslStep2 = Sarissa.getDomDocument();
        oXslStep2.async = false;
        oXslStep2.load("xslt2.xsl");
        
      function showPage(first, size)
      {
        var xsltProc1  = new XSLTProcessor();
        xsltProc1.importStylesheet(oXslStep1);
        var xsltProc2  = new XSLTProcessor();
        xsltProc2.importStylesheet(oXslStep2);
        xsltProc2.setParameter('', 'first' , first);
        xsltProc2.setParameter('', 'size' , size);
        var step1Result = xsltProc1.transformToDocument(oXmlDoc);
        var newDoc = xsltProc2.transformToDocument(step1Result);
        document.getElementById("RECORDS").innerHTML = new XMLSerializer().serializeToString(newDoc);
      }
</script>
</head>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36568737
link is not OK, I think it should be http://#a36568320
0
 

Author Comment

by:help-is-needed
ID: 36572543
Hi gertone,

thanks for getting back.

I'm still getting some funky layouts for my results, and in IE7 & 8 im getting an error - Only one top level element is allowed in an XML document.

I've attached my project so you can emulate it fully.
xml.txt
test-refine.txt
test2.txt
sarissa.txt
0
 

Author Comment

by:help-is-needed
ID: 36574140
Hi gertone,

I managed to get around the oddities with the results display by displaying them in a table rather than a series of positioned divs.

Is there anyway I can get around the need for two xslts? If I can combine the whole process into one xslt then that would be this project complete. I only say this because the server this will be hosting on has conversion script on there for other XML XSLT transformations already and looks at one XML and one XSLT. So the implementation of this would be a hell of a lot easy if I could stick with that layout?

Is this possible?

thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36574447
I was looking at your files by the way and the main issue for IE is the fact that sarissa requires one root in the transform result (transformix in FF gets around that issue by making a forced root)
If you move this to a server, you don't have that problem

Have you changed the second XSLT?
I can change that for not needing step one, at the cost of more complex XSLT
0
 

Author Comment

by:help-is-needed
ID: 36579033
Hi,

Yes I changed the second XSLT. Not the way it works though, just the layout of the transformation.

If you could change that so it uses just one XSLT, that would be brilliant.

Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36579087
send in the XSLT, please, so I can change it to work in one go,
can take a couple of hours, am busy at the moment
0
 

Author Comment

by:help-is-needed
ID: 36579200
hi Gertone, you can work off the XSL2 I attached earlier.
'test2.txt'

http://#a36572543

Thanks

0
 

Author Comment

by:help-is-needed
ID: 36579273
Also, if you can introduce the ability to sort that would be great too.

Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36579303
There were two options

- filter the nodes at every level
- create a multipass in one stylesheet (but then the stylesheet will only work for a specific XSLT processor then)

By introducing sort on a paging stylesheet you have just taken away the first option
I hope you can accept that your stylesheet willonly work for a dedicated XSLT processor
You have to tell me then which processor you are using
0
 

Author Comment

by:help-is-needed
ID: 36579456
The set up I am using is all in the files I attached earlier. That is essentially how I will be running the script.

I also need to be able to filter the results so that only certain destinations appear.
With this split into two files it makes it difficult for me to get my head around this as I would need to do it in the initial xslt so that the pagination is correct in the second.

I understand the logic of how to filter the results, i'll have a variable that will be passed in and then choose statement.

Do you think you will be able to combine this all into one xslt?

Thanks

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36579473
Well, you have to make a choice
- Sarissa = different browsers = different XSLT processors = processor independent XSLT required = two steps
- Server = a single XSLT processor = processor dependent XSLT possible = one step possible (though using node-sets, being processor dependent)
I can't do both, definitely not in the scope of an EE question

This statement of yours contradicts with the Sarissa request:
"I only say this because the server this will be hosting on has conversion script on there for other XML XSLT transformations already and looks at one XML and one XSLT."
Sarissa is client XSLT, server means likely server XSLT
0
 

Author Comment

by:help-is-needed
ID: 36579592
Hi Gertone,

Thanks. OK, well lets assume I keep using Sarissa and that we keep it at two xslts for the time being then.

how would I adapt it so that I can add sort and filter functionality?
Ideally I would like to be able to sort by ServiceName, Product_Rating and Price.
The filtering will be done on the Desc_Destinations field.

Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36580604
OK, what exactly do you need?
Do you need to select the sort field?
So you would have buttons or radio buttons that help you select between ServiceName, Product_Rating, ...
What do you mean with filtering? Have a dropdown that allows you to select a value from the available values?

I have done that a couple of times using Sarissa,
I can explain the approach now, but can't develop this today since I have too much other stuff to do...
but it would get you started

Definitely have two steps
extend the transformation function with two extra parameters, one selecting the sort filed, and one that passes the value for Desc_Destinations
In the first step (pass in the sort information and the Desc_Destination chosen value)
- make a list of all the distinct values of Desc_Destinations for the dropdown (after filtering the second XSLT would not know about the other values)
- filter out the empty records
- filter out the records you have not selected
- sort by the chosen field (if field name = "sortfield" you can sort like this <xsl:sort select="*[name() = $sortfield]"/>)
In the second step (pass in size and start point)
- make the layout as you do now
- create the form fields for sortfield selection and desc filtering (from "hidden" information you sneaked in during the first transform)
- do the paging

This is a pretty common usecase by the way, even on a server it makes sense to have this in two steps
- one step for sorting and filtering
- one step for actual paging
0
 

Author Comment

by:help-is-needed
ID: 36581253
Ok thanks Gertone,

I'll try and have a play around this evening. If i get stuck I will let you know.

In reply to your questions...
OK, what exactly do you need?
Do you need to select the sort field?
So you would have buttons or radio buttons that help you select between ServiceName, Product_Rating, ...
What do you mean with filtering? Have a dropdown that allows you to select a value from the available values?

Yeah to select the sort field would be ideal. Perhaps a radiobutton or just text link.
By filtering I mean that if I wish to limit the results to display just Product_Rating = 5, or Desc_Destination = Florida.

I'll keep, as you suggest, as two transformations and attempt some of the stages you have mentioned above and see how I get on. If you manage to knock up any code that may help me then that would be fantastic.

Thanks
0
 

Author Comment

by:help-is-needed
ID: 36581436
fallen at the first hurdle...

I understand I need to modify the initial xslt for the sorting and filtering but not sure where to add the code in as it looks like this...

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>
    <xsl:template match="node()">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>
  <xsl:template match="Product[not(normalize-space(Story/ServiceName)) or not(normalize-space(Story/Desc_Destinations)) or not(normalize-space(Story/Product_Rating))]" />
 </xsl:stylesheet>

Where in here do I need to add the sort and filter code?
0
 

Author Comment

by:help-is-needed
ID: 36586160
Hi Gertone,

Ok, to make this even simlper I have eliminated the need for the first xslt and have requested a clean XML feed so there is no need to check the XML first.

So now I will try and work on the filtering and sorting.

Thanks
0
 

Author Comment

by:help-is-needed
ID: 36586231
Sorry Gertone,

Im thinking out loud a bit here.
I'll still need the first xslt to do my filtering and sorting wont I?! Otherwise the pagination will be wrong.

Am I correct.
Will you be able to provide me with some code to get started on the sorting and filtering as struggling to get going?

Many thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36586330
yes, you still need the first XSLT for filtering and sorting

I can help you with the code, but not immedeatly,
busy doing some work for customers
0
 

Author Comment

by:help-is-needed
ID: 36586839
Ok, thanks.
0
 

Author Comment

by:help-is-needed
ID: 36596073
Hi gertone,

Were you able to look into this?

Thanks
0
 
LVL 60

Assisted Solution

by:Geert Bormans
Geert Bormans earned 500 total points
ID: 36710729
This should give you a basis to work on
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>
  <xsl:key name="prod-by-rating" match="Product" use="Product_Rating"/>
  <xsl:template match="node()">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="Product[not(normalize-space(Story/ServiceName)) or not(normalize-space(Story/Desc_Destinations)) or not(normalize-space(Story/Product_Rating))]"/>
  <xsl:template match="Root">
    <Root>
      <dropdowns>
        <dropdown name="product_rating">
          <!-- muenchian for getting unique terms -->
          <xsl:for-each select="Product[generate-id() = generate-id(key('prod-by-rating', Product_Rating)[1])]">
            <term>
              <xsl:value-of select="Product_Rating"/>
            </term>
          </xsl:for-each>
        </dropdown>
        <!-- same for desc_destination -->
        <!-- dropdown elements kan be used for creating dropdown in xslt2 -->
        <xsl:apply-templates select="ProductProduct[not(normalize-space(Story/ServiceName)) or not(normalize-space(Story/Desc_Destinations)) or not(normalize-space(Story/Product_Rating))]['extra filtering goes here']"><!-- select does filtering in predicate -->
          <xsl:sort/><!-- dynamic sort goes here -->
        </xsl:apply-templates>
      </dropdowns>
    </Root>
    
  </xsl:template>
</xsl:stylesheet>

Open in new window

0

Featured Post

DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Browsing the questions asked to the Experts of this forum, you will be amazed to see how many times people are headaching about monster regular expressions (regex) to select that specific part of some HTML or XML file they want to extract. The examp…
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
This video shows how to quickly and easily add an email signature for all users on Exchange 2016. The resulting signature is applied on a server level by Exchange Online. The email signature template has been downloaded from: www.mail-signatures…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

778 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question