Sorting and Paging

Sort and Page Presented Data

In this article, you will learn about sorting and paging when outputting data with XSLT. The output lists 50 U.S. states in a table splitting them between 5 pages, 10 states per page.

The user can click the headings in the table to sort the data by that field as well as navigate the pages by clicking the Prev/Next links.

This sample is available as a ready-to-use package, which you can download, install and play with:

Datatype

As the data source, we are using a global data type called "Sample.Paging.UsState". It has 3 string fields:

  • Abbreviation
  • Name
  • Capital

There are 50 items, each standing for one state.

XSLT

To output the states as a table, we have created an XSLT function - Sample.Paging.ListStates. (After you have installed the package, insert this function on a page and see what it does.)

To access the data in the Sample.Paging.UsState data type, we have added a function call to Sample.Paging.UsState.GetUsStateXml in our function.

When you create a data type in C1 CMS, the system automatically generates a function that outputs its data items as an enumerable list of XML elements. The name of the function follows the "Get(Data)Xml" pattern where '(Data)' stands for the name of the data type.

Having added a call to the Get(Data)Xml function, you can access the data items in XSLT as XML:

<in:inputs xmlns:in="http://www.composite.net/ns/transformation/input/1.0">
	<in:result name="GetUsStateXml">
		<UsState Abbreviation="AL" Name="Alabama" Capital="Montgomery" xmlns=""/>
		<UsState Abbreviation="AK" Name="Alaska" Capital="Juneau" xmlns=""/>
		<!-- other items -->
	</in:result>
</in:inputs>

In the sample, we iterate the data items to fill the table:

<table>
	<xsl:for-each select="$items/UsState" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
		<tr>
			<td>
				<xsl:value-of select="@Abbreviation" />
			</td>
			<td>
				<xsl:value-of select="@Name" />
			</td>
			<td>
				<xsl:value-of select="@Capital" />
			</td>
		</tr>
	</xsl:for-each>
</table>

(In the markup above, we are using the "$items" variable that stands for "/in:inputs/in:result[@name='GetUsStateXml']".)

Get(Data)Xml Function Parameters

The Get(Data)Xml function has a number of parameters. Here we will discuss the parameters used for paging and sorting in the sample.

First, you need to select what fields should be included in the output. Use this parameter:

  • Selected fields: The data fields to output in the XML. 

(We have selected "Abbreviation", "Name" and "Capital" for our sample.)

Second, you need to set up paging in the output. Use the following parameters:

  • Page size:The number of items to display on one page – the maximum number of elements to return.
  • Page number: If the number of data elements exceed the page size you can use paging to move to the other pages.
  • Include paging info: When 'true', the data XML will be preceded by a <PagingInfo /> element detailing number of pages, items and more.

Third, you need to set up sorting by the selected fields. Use these parameters:

  • Order by: The field to order data by
  • Order ascending: When 'true', results are delivered in ascending order; otherwise, descending order is used. Default is ascending order.

Paging

We have set the GetUsStateXml's PageSize parameter to 10 so that there are 5 pages, each displaying 10 of 50 states.

We are using an URL parameter "Page" to switch between the pages.

Example:

http://www.contoso.com/States?OrderBy=Abbreviation&Page=2

When the user clicks the Prev or Next links (if available), the value of the "Page" parameter in the URL decreases or increases (if possible), causing to display the proper range of U.S. states on the page. That is why, instead of hard-coding a specific page number, we have set the GetUsStateXml's Page number parameter to call the Composite.Web.Request.QueryStringIntegerValue function, which queries the "Page" parameter in the URL for its value. (The fallback value is "1".)

In XSLT, we generate a link to the Prev and Next pages in this manner:

<xsl:template match="PagingInfo" mode="Navigation" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<div class="PagingInfo">
		<xsl:choose>
			<xsl:when test="@CurrentPageNumber &gt; 1">
				<xsl:text>&lt; </xsl:text>
				<a href="/page({$pageId})?OrderBy={$orderBy}&amp;Page={@CurrentPageNumber - 1}">Prev</a>
			</xsl:when>
			<xsl:otherwise>
				<xsl:text>&lt; Prev</xsl:text>
			</xsl:otherwise>
		</xsl:choose>
		<xsl:choose>
			<xsl:when test="@CurrentPageNumber &lt; @TotalPageCount">
				<a href="/page({$pageId})?OrderBy={$orderBy}&amp;Page={@CurrentPageNumber + 1}">Next</a>&#160;&gt;
			</xsl:when>
			<xsl:otherwise>Next &gt;</xsl:otherwise>
		</xsl:choose>
	</div>
</xsl:template>

Besides, we have XSLT auto-generate links to each page as a paging list (1, 2, 3, 4, 5):

<xsl:template match="PagingInfo" mode="PagingList" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
	<xsl:param name="page" select="1"></xsl:param>
	<xsl:choose>
		<xsl:when test="@CurrentPageNumber != $page">
			<a href="/page({$pageId})?OrderBy={$orderBy}&amp;Page={$page}">
				<xsl:value-of select="$page"/>
			</a>
		</xsl:when>
		<xsl:otherwise>
			<span>
				<xsl:value-of select="$page"/>
			</span>
		</xsl:otherwise>
	</xsl:choose>
	<xsl:if test="$page &lt; @TotalPageCount">
		<xsl:apply-templates select="." mode="PagingList">
			<xsl:with-param name="page" select="$page + 1" />
		</xsl:apply-templates>
	</xsl:if>
</xsl:template>

In the sample above, a recursion is used to generate links to each page in the list. A link is not generated for the currently selected page (see <xsl:choose> ... </xsl:choose>).

Paging info

As we have set the Include paging info parameter to 'true', the following element has been included in XML emitted by the GetUsStateXml function:

<PagingInfo CurrentPageNumber="1" TotalPageCount="5" TotalItemCount="50" ShownItemsCount="10" 
MaximumItemsPerPage="10" CurrentItemNumberStart="1" CurrentItemNumberEnd="10" xmlns=""/>

The names of the attributes are self-explanatory. We are using their values to show the status information on a page:

  • What page is currently displayed

    <xsl:template match="PagingInfo" mode="Navigation" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    	<xsl:text> | Page </xsl:text>
    	<xsl:value-of select="@CurrentPageNumber" />
    	<xsl:text> of </xsl:text>
    	<xsl:value-of select="@TotalPageCount" />
    	<xsl:text> | </xsl:text>
    </xsl:template>

  • What range of items is currently displayed:

    <xsl:template match="PagingInfo" mode="Info" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    	<div class="ItemInfo">
    		<xsl:text>Showing Items </xsl:text>
    		<xsl:value-of select="@CurrentItemNumberStart" />
    		<xsl:text> to </xsl:text>
    		<xsl:value-of select="@CurrentItemNumberEnd" />
    		<xsl:text> of </xsl:text>
    		<xsl:value-of select="@TotalItemCount" />
    	</div>
    </xsl:template>

Sorting

We are using another URL parameter "OrderBy" to sort the items in the table by one of the 3 fields.

Example:

http://www.contoso.com/States?OrderBy=Abbreviation&Page=2

When the user clicks the heading in the table, the items get sorted in ascending order by that very field. Like for paging, we have set the GetUsStateXml's Order by parameter to call the Composite.Web.Request.QueryStringValue function, which queries the "OrderBy" parameter in the URL for its value. (The fallback value is "Name".)

Please note that since all the items get sorted, the first page will always be shown when the user clicks the heading.

In XSLT, the sorting is implemented as follows:

<table class="StateList">
	<tr>
		<th>
			<a href="/page({$pageId})?OrderBy=Abbreviation">Abbreviation </a>
		</th>
		<th>
			<a href="/page({$pageId})?OrderBy=Name">Name</a>
		</th>
		<th>
			<a href="/page({$pageId})?OrderBy=Capital">Capital</a>
		</th>
	</tr>
</table>