1

Need once more a litle bit of help on (re) paginate that already mentioned simple xml table data (XML table columns vertically stacked within rows) but transposed this time as Michael Hor sugested over the above SO's second answer post up, but in addition with displaying three records per (mobile) page. Need this for a mobile pages..
The xml file goes something like this:

<persns> 
 <prsn> 
  <fname>Smith</fname> 
  <lname>Milton</lname> 
  <age>44</age> 
  <addrss>5th summer st, mntb</addrss>
  <city>Portland</city>
 </prsn>
 <prsn> 
  <fname>Ken</fname> 
  <lname>Jackson</lname> 
  <age>37</age> 
  <addrss>19th Penfield ave, brtcl</addrss>
  <city>Kelowna</city>
 </prsn>
 <prsn> 
  <fname>Susan</fname> 
  <lname>Arkland</lname> 
  <age>48</age> 
  <addrss>34th Mansfield st, sgtp</addrss>
  <city>Raleigh</city>
 </prsn>
 <prsn> 
  <fname>George</fname> 
  <lname>Bond</lname> 
  <age>35</age> 
  <addrss>5th drive, mntb</addrss>
  <city>Albany</city>
 </prsn>
 <prsn> 
  <fname>Ron</fname> 
  <lname>Davis</lname> 
  <age>37</age> 
  <addrss>12th Greenfield ave, brtcl</addrss>
  <city>Pheonix</city>
 </prsn>
</persns>

The way displaying is needed is as follows:

|===========|---------------|-------------|------------|  
|   FNAME   |   Smith       |  Ken        |   Susan    |
|===========|---------------|-------------|------------|
|   LNAME   |   Milton      |  Jackson    | Arkland    |
|===========|---------------|-------------|------------|
|    AGE    |     44        |     37      |    48      |
|===========|---------------|-------------|------------|
| ADDRESS   |5th smmr st,mntb9th Pnfeld ave 34th Mansfield st       |===========|---------------|-------------|------------|                                        
|    CITY   |   Portland    |   Kelowna   |  Raleigh   |
|===========|---------------|-------------|------------|
|                  <<   <  1/4  >  >>                  |
|=======================================================
               Fig 1 First Page
|===========|---------------|-------------|------------|  
|   FNAME   |  George       |    Ron      | Marie-Ann  |
|===========|---------------|-------------|------------|
|   LNAME   |    Bond       |    Davis    | Spencer    |
|===========|---------------|-------------|------------|
|    AGE    |      35       |    37       |    48      |
|===========|---------------|-------------|------------|
| ADDRESS   |5th drive, mntb|12th Greenfld ave 273 Simpson square                
|===========|---------------|-------------|------------|                                        
|    CITY   |   Albany      |    Pheonix  |  Oklahoma  |
|===========|---------------|-------------|------------|
|                  <<   <  2/4  >  >>                  |
|=======================================================
            Fig 2 Second Page

Exactly as Michael Hor suggested through that previous post's answer. And that stylesheet is somehow as follows:

<xsl:param name="frame" select="3"/>
<xsl:template match="persns">
<xsl:apply-templates select="prsn[position() mod $frame=1]">
<xsl:with-param name="pags" select="ceiling(count(prsn) div$frame)"/>    
</xsl:apply-templates>
</xsl:template>

<xsl:template match="/prsn">
<xsl:variable name="rec" select="prsn"/>
 <table border="1">
  <xsl:for-each select="prsn[1]/*">
<xsl:variable name="pag" select="position()"/>
<tr><th><xsl:value-of select="name()"/></th>
 <xsl:for-each select="$rec">
  <td><xsl:value-of select="*[$pag]"/> </td>
 </xsl:for-each>
 </tr>
</xsl:for-each>
</table>
 </xsl:template>

As one can easily notice the second template - just by itself - would correctly displays All the xml records. And it does it exactly the way I wanted to, but without pagination. If I actually insert the first template (on the other hand) where is xsl:apply-templates declaration and "with param" stuff everything gets "mixed up"; as first template - by itself - displays that set of 3 records (or something) with no formatting and ignoring the second template which is suppose to display records in a formated manner. Don't know why those two templates are somehow "mutually exclusive"
After that it comes the pagination section area, and a couple of small js functions bound over those <a href links. All these could be found here: one record at a time xml pagination on xslt This would be it for now. I really struggled for weeks for properly display all these.
Thank you guys în advance.

mirexS
  • 27
  • 4

1 Answers1

0

I suppose the element names are known; I also think that most XSLT 2 processors (e.g. Saxon) these days in its current versions (10, 11, 12) support XSLT 3 so you could use XSLT 3 as

<?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="#all"
    expand-text="yes"
    version="3.0">
  
  <xsl:param name="items-per-page" as="xs:integer" select="3"/>
  
  <xsl:template match="persns">
    <xsl:variable name="data-arrays" as="array(map(*)*)*">
          <xsl:for-each-group select="prsn" group-adjacent="(position() - 1) idiv $items-per-page">
            <xsl:sequence select="array { current-group() ! map { 'fname' : data(fname), 'lname' : data(lname), 'age' : data(age), 'address' : data((addrss, ciyt)) }}"/>
          </xsl:for-each-group>
    </xsl:variable>
    <xsl:for-each select="$data-arrays">
      <table class="page" style="display: {if (position() eq 1) then '''''' else 'none'}">
        <tbody>
          <xsl:variable name="data-maps" select="?*"/>
          <tr>
            <th>fname</th>
            <xsl:for-each select="$data-maps?fname">
              <td>{.}</td>
            </xsl:for-each>
          </tr>
          <tr>
            <th>lname</th>
            <xsl:for-each select="$data-maps?lname">
              <td>{.}</td>
            </xsl:for-each>
          </tr>
          <tr>
            <th>age</th>
            <xsl:for-each select="$data-maps?age">
              <td>{.}</td>
            </xsl:for-each>
          </tr>
          <tr>
            <th>address</th>
            <xsl:for-each select="$data-maps?address">
              <td>{.}</td>
            </xsl:for-each>
          </tr>
        </tbody>
      </table>      
    </xsl:for-each>
    <nav>
      <input type="button" value="&lt; previous page" onclick="previousPage()"/>
      <input type="button" value="> next page" onclick="nextPage()"/>
    </nav>
  </xsl:template>

  <xsl:mode on-no-match="shallow-copy"/>

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

  <xsl:template match="/">
    <html>
      <head>
        <title>.NET XSLT Fiddle Example</title>
        <script xsl:expand-text="no">
        var currentPageIndex = 0;
        var pages;
        
        function nextPage() {
          pages[currentPageIndex].style.display = 'none';
          currentPageIndex++;
          if (currentPageIndex > lastPageIndex)
            currentPageIndex = 0;
          pages[currentPageIndex].style.display = '';
        }
        
        function previousPage() {
          pages[currentPageIndex].style.display = 'none';
          currentPageIndex--;
          if (currentPageIndex &lt; 0)
            currentPageIndex = lastPageIndex;
          pages[currentPageIndex].style.display = '';
        }
        
        var lastPageIndex = -1;
        
        document.addEventListener('DOMContentLoaded', 
          function(evt) {
            pages = document.getElementsByClassName('page');
            lastPageIndex = pages.length - 1;
          },
          false
        );
        </script>
        <style xsl:expand-text="no">
          table, tr, td, th { border: 1px solid black; }
        </style>
      </head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  
</xsl:stylesheet>

I haven't disabled the previous/next page buttons, instead let next page if showing the last one go to page 1 and previous page if showing the first page go to the last page.

Here is a link to the SaxonJS, browser-side XSLT fiddle having the sample code.

A quick attempt to transcribe the XSLT 3 to 2 is below, I haven't tested it with an XSLT 2 processor, however:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="2.0">
  
  <xsl:param name="items-per-page" as="xs:integer" select="3"/>
  
  <xsl:template match="persns">
    <xsl:variable name="data-groups" as="element(group)*">
          <xsl:for-each-group select="prsn" group-adjacent="(position() - 1) idiv $items-per-page">
            <group>
              <xsl:sequence select="current-group()"/>
            </group>
          </xsl:for-each-group>
    </xsl:variable>
    <xsl:for-each select="$data-groups">
      <table class="page" style="display: {if (position() eq 1) then '''''' else 'none'}">
        <tbody>
          <xsl:variable name="data-elements" select="*"/>
          <tr>
            <th>fname</th>
            <xsl:for-each select="$data-elements/fname">
              <td>
                <xsl:value-of select="."/>
              </td>
            </xsl:for-each>
          </tr>
          <tr>
            <th>lname</th>
            <xsl:for-each select="$data-elements/lname">
              <td>
                <xsl:value-of select="."/>
              </td>
            </xsl:for-each>
          </tr>
          <tr>
            <th>age</th>
            <xsl:for-each select="$data-elements/age">
              <td>
                <xsl:value-of select="."/>
              </td>
            </xsl:for-each>
          </tr>
          <tr>
            <th>address</th>
            <xsl:for-each select="$data-elements/concat(addrss, ', ', city)">
              <td>
                <xsl:value-of select="."/>
              </td>
            </xsl:for-each>
          </tr>
        </tbody>
      </table>      
    </xsl:for-each>
    <nav>
      <input type="button" value="&lt; previous page" onclick="previousPage()"/>
      <input type="button" value="> next page" onclick="nextPage()"/>
    </nav>
  </xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:output method="html" indent="yes" html-version="5" doctype-system="about:legacy-doctype"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>.NET XSLT Fiddle Example</title>
        <script>
        var currentPageIndex = 0;
        var pages;
        
        function nextPage() {
          pages[currentPageIndex].style.display = 'none';
          currentPageIndex++;
          if (currentPageIndex > lastPageIndex)
            currentPageIndex = 0;
          pages[currentPageIndex].style.display = '';
        }
        
        function previousPage() {
          pages[currentPageIndex].style.display = 'none';
          currentPageIndex--;
          if (currentPageIndex &lt; 0)
            currentPageIndex = lastPageIndex;
          pages[currentPageIndex].style.display = '';
        }
        
        var lastPageIndex = -1;
        
        document.addEventListener('DOMContentLoaded', 
          function(evt) {
            pages = document.getElementsByClassName('page');
            lastPageIndex = pages.length - 1;
          },
          false
        );
        </script>
        <style xsl:expand-text="no">
          table, tr, td, th { border: 1px solid black; }
        </style>
      </head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>
  
</xsl:stylesheet>

Online at http://xsltransform.net/bEzkntf where Saxon 9.5 works as the XSLT 2 processor.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thank you very much mr. Honnen for your reply, but, unfortunately over my mobile phone ain't got no xslt 3 processor.. That's why I kindly ask you for a xslt 2 solution, as I'm currently using frameless xslt 2 processor which really works pretty well so far. Thanks very much anyway! – mirexS Jan 16 '23 at 20:16
  • SaxonJS 2.5 https://www.saxonica.com/saxon-js/index.xml is an XSLT 3 processor that would work in the browser, whether it is on a desktop or on a mobile. https://www.saxonica.com/saxon-js/documentation2/index.html. Sorry about the XSLT 3 then but I didn't expect the use of something relatively rare like frameless without explicitly mentioning it in the question. – Martin Honnen Jan 16 '23 at 20:20
  • Oh sir, don't need to apologise, it's me I have to be excused on not being clear enough. But, again if you can help me with a xslt 2 solution I'll greatly appreciate it. Thank you very much in advance for your help and time – mirexS Jan 16 '23 at 20:36
  • Actually as on my understanding, on saxonjs first I have to compile that initial .xsl to some other format in order to correctly being interpreted by saxonjs processor. Am I correct on this or I had misunderstood the whole thing... – mirexS Jan 16 '23 at 20:40
  • It depends on how you approach it, the pure, smaller run-time library `SaxonJS2.rt.js` of SaxonJS processes precompiled SEF files or strings (you can precompile with the free Node.js `xslt3` command line tool you can find on NPM https://www.npmjs.com/package/xslt3) but the larger SaxonJS library `SaxonJS2.js` allows you to call the XPath 3.1 `fn:transform` function to apply XSLT 3 from a file or string, that is how my XSLT 3 fiddle works where users can enter their XSLT 3 code and the tool uses `fn:transform` to run the transformation. – Martin Honnen Jan 16 '23 at 21:20
  • Please you guys, can you provide some simpler xslt 2 solution for this starting eventually from one here.. I'm in very much need for that and the current solution simply's not working for me. Thanks again in advance. – mirexS Jan 17 '23 at 15:50
  • @mirexS, see whether the edit helps, it has the grouping converted to regular XSLT 2 grouping with intermediary results stored as XML elements, not XPath 3.1 maps/arrays. I haven't tested it and now that I see it, I guess you need to remove the `xsl:expand-text="no"`. – Martin Honnen Jan 17 '23 at 16:37
  • @M.Honnen Well, it doesn't work so far... with frameless processor... Feel very embarrassed on keep asking for a simpler solution starting from what's already posted; but thing is I'm particularly interested why (up in my original stylesheet) the second template is simply ignored by the processor, displaying the first eight or nine xml nodes (with that apply-templates's select condition) but ain't got any chance of creating some sort of html table (or something similar) through the second template. Please don't get me wrong, your solution is great, but a little bit advanced for me... – mirexS Jan 17 '23 at 17:34
  • Almost... _______ |fname| lname |age | |address| [,] [,] [,] [,] [,] [,] [,] [,] [,] - - - - - - - - - - - - - - - - - - - - - - - - - prev next --------------------------- Aș one could notice instead of actual xml nodes values the sheet printed only 9 commas.. And the buttons below doesn't seems to work.. Seems that it doesn't includes to jump over the next line in this comment..... – mirexS Jan 17 '23 at 19:49
  • Right! It's working. Thank you very oh so much. I really appreciate it a great deal. And please sorry for my bother. Best regards – mirexS Jan 17 '23 at 20:41
  • Could anyone please shows a working solution without grouping (or at least with Munchenian grouping) as I just can't use xslt2 through my current xslt2 proc. Very sorry on keep asking this, but I really, really need this very badly for being through some sort of time crisis, you know ... Thanks again in advance. – mirexS Jan 24 '23 at 08:13
  • I commented on your latest question and pointed you to an existing answer that has both XSLT 2 and XSLT 1 code where the XSLT 1 code simply select the current node `.` unioned with `following-sibling::*[position() < $size]`, that is an approach that would work even with any XSLT 1.0 processor. But your constantly ignoring hints you get as to how to fix or approach things, sorry, but in the end we can't hold your hand and have it write your code for you. – Martin Honnen Jan 24 '23 at 08:28