Solved

adding page numbers to xslt pagination

Posted on 2011-09-20
33
250 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
Comment Utility
<xsl:value-of select="floor($first div $size)"/>
will likely give you the number you need
0
 

Author Comment

by:help-is-needed
Comment Utility
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
Comment Utility
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
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
can you show me the sarissa javascript code you allready have?
0
 
LVL 60

Expert Comment

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

Expert Comment

by:Geert Bormans
Comment Utility
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
Comment Utility
link is not OK, I think it should be http://#a36568320
0
 

Author Comment

by:help-is-needed
Comment Utility
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
Comment Utility
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
Comment Utility
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
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:help-is-needed
Comment Utility
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
Comment Utility
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
Comment Utility
hi Gertone, you can work off the XSL2 I attached earlier.
'test2.txt'

http://#a36572543

Thanks

0
 

Author Comment

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

Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Ok, thanks.
0
 

Author Comment

by:help-is-needed
Comment Utility
Hi gertone,

Were you able to look into this?

Thanks
0
 
LVL 60

Assisted Solution

by:Geert Bormans
Geert Bormans earned 500 total points
Comment Utility
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

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Suggested Solutions

The Confluence of Individual Knowledge and the Collective Intelligence At this writing (summer 2013) the term API (http://dictionary.reference.com/browse/API?s=t) has made its way into the popular lexicon of the English language.  A few years ago, …
Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

772 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now