Tech tutorials Advanced Paging With SharePoint Content Search Web Part Display Templates
By Insight Editor / 28 May 2015 , Updated on 21 Aug 2019
By Insight Editor / 28 May 2015 , Updated on 21 Aug 2019
I’ve had an opportunity to play with the SharePoint 2013 Content Search Web Part (CSWP) on a number of occasions, and I have to say I like it a lot. The CSWP can be employed to address a whole host of different use cases. In fact, in many situations, I’ve found it can be used to solve problems that were previously addressable only through custom code.
Search-driven content in SharePoint isn’t anything new, of course, but the display templates used to format search results in SharePoint 2013 are a large part of the CSWP’s “special sauce.” Through the use of a Control display template and an Item display template, it’s possible to select, arrange and style the search results that are returned and shown to users in a highly customizable fashion.
With some knowledge of HTML and the help of the SharePoint 2013 Design Manager, you can produce some pretty impressive looking content. And, if you know JavaScript, well … the sky is the limit on what you can produce.
Before I dive into paging and how I’ve tried to stretch what can be done with the CSWP, I want to briefly examine some display template basics. Although this article isn’t intended to be a primer on the CSWP and its associated display templates, there are a few items worth reviewing.
On the right is a snippet of a ToolPart containing some configuration data for a particular CSWP displaying some very basic data. The examples that follow (i.e., the two images shown below) use the configuration shown in the ToolPart, so refer to it if needed.
The first example below is how a CSWP showing three results might appear to an end user when the List with Paging Control template and Two lines Item template are applied. The second example differentiates which content regions on the CSWP are being driven by the List with Paging Control template (shown with green highlighting) and which are being driven by the Two lines Item template (shown in red).
Through these images, I’m trying to convey a very simple point: The Control template dictates how the search results’ “container” appears, and the Item template determines how each individual search result within the container is displayed. The contents of any CSWP are rendered by the output of a single Control display template and zero or more Item display templates (again, one for each search result/item shown).
As you might have guessed from the title of this article, one functional area that seems relatively unexplored and underdeveloped (in my experience) is that of paging and how paging controls are made available to end users of the CSWP. This is unfortunate because whenever more results are returned than can be displayed at once — a very common scenario — some form of paging is needed.
Out of the box, SharePoint 2013 comes with only a handful of Control and Item display templates that can be used with the CSWP to format your search results. Of these display templates, only one contains paging controls: the List with Paging Control template. If all you require is the basic forward/backward paging offered by the two buttons it contains, then the List with Paging display template may be adequate for your needs.
Personally, I think the List with Paging template is bland and kind of … ugly. It works, sure, but it doesn’t display several pieces of information I find important — for example, the total number of search results and the result page the user is currently on. Worse is the fact that it doesn’t provide any sort of mechanism to jump to a specific page. The best that an end user can do is page forward or backward one page at a time.
<script src="/sites/a2webpart/SiteAssets/dist/wp1.min.js"></script>
<web-part-1>Loading...</web-part-1>
<script>
System.import('src/app/wp1').catch(function(err){ console.error(err); });
</script>
One of the talks I’ve been giving at various SharePoint events and conferences is titled “SharePoint’s New Swiss Army Knife: The Content Search Web Part.” During that talk, I demonstrate a set of CSWP display templates I put together to generate something decidedly “nonsearch” in appearance — such as a directory of files to which the current user has access within the current site collection. An image of that CSWP example appears above.
This file directory CSWP instance was generated with three simple files: two custom display templates (one for the Control, and one for each Item shown) and a cascading style sheet. The actual result data isn’t particularly remarkable, but the manner in which the paging is implemented is what tends to catch people’s attention. With the Control template I created, end users can:
Finding all of this information and displaying it through the CSWP Control template took some research and tinkering. Some of the information was available directly within the search results that were passed to the CSWP, but some of it wasn’t. In the case where some desired information wasn’t available, it was computed with some basic math.
By default, the CSWP implements a client-side processing and paging model. Search results displayed are controlled by JavaScript functions contained within the Control and Item display templates that have been assigned to the CSWP. When a set of search results is being processed for display within the client browser, the Control template (which is the results container) gets called first to render the HTML that will frame or house the search items/results.
For each search result or item that is passed to the CSWP, the Item display template then gets called to create a snippet of HTML for the result/item that can be inserted into the “frame” (commonly a <div>) created by Control display template. An example of the rendered HTML for the search results shown in the previous “Better Paging Support” section appears below.
<div id="sas-lib-listing">
<div>Documents To Which I Have Access</div>
<ul class="sas-table">
<li class="sas-tablerow">
<span class="sas-tableheader">Title of Document</span>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr1_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr1_SAS_Item_Template_dataContainer">
<a title="3. Optimus Prime Part - Button" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr1_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SiteAssets/Forms/DispForm.aspx?ID=4">3. Optimus Prime Part - Button</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr2_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr2_SAS_Item_Template_dataContainer">
<a title="4. Weather Right Now (Not Really) - Button" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr2_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SiteAssets/Forms/DispForm.aspx?ID=5">4. Weather Right Now (Not Really) - Button</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr3_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr3_SAS_Item_Template_dataContainer">
<a title="5. Weather Right Now Page - Button" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr3_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SiteAssets/Forms/DispForm.aspx?ID=6">5. Weather Right Now Page - Button</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr4_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr4_SAS_Item_Template_dataContainer">
<a title="6. Weather Right Now Provisioned - Button" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr4_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SiteAssets/Forms/DispForm.aspx?ID=8">6. Weather Right Now Provisioned - Button</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr5_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr5_SAS_Item_Template_dataContainer">
<a title="8. Donuts And Drinks Pub - Button" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr5_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SiteAssets/Forms/DispForm.aspx?ID=9">8. Donuts And Drinks Pub - Button</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr6_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr6_SAS_Item_Template_dataContainer">
<a title="WeatherRightNowScraper" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr6_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SitePages/WeatherRightNowScraper.aspx">WeatherRightNowScraper</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr7_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr7_SAS_Item_Template_dataContainer">
<a title="DonutsAndDrinksPub" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr7_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/Pages/DonutsAndDrinksPub.aspx">DonutsAndDrinksPub</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr8_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr8_SAS_Item_Template_dataContainer">
<a title="DonutsAndDrinks" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr8_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SitePages/DonutsAndDrinks.aspx">DonutsAndDrinks</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr9_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr9_SAS_Item_Template_dataContainer">
<a title="MakingPiTakesEffort" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr9_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SitePages/MakingPiTakesEffort.aspx">MakingPiTakesEffort</a>
</div>
</div>
</li>
<li class="sas-tablerow">
<div class="sas-tablecell" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr10_SAS_Item_Template_container" data-displaytemplate="SAS_Item_Template">
<div id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr10_SAS_Item_Template_dataContainer">
<a title="OptimusPrimePart" id="ctl00_ctl39_g_85d5f2f9_fbe4_4e9b_9d51_647e179b87f9_csr10_SAS_Item_Template_displayTextLink"
href="http://sp2013-dev:18480/SitePages/OptimusPrimePart.aspx">OptimusPrimePart</a>
</div>
</div>
</li>
</ul>
<div class="sas-paging-table">
<div class="sas-paging-tablerow">
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(1);return Srch.U.cancelEvent(event);" href="#">1</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(11);return Srch.U.cancelEvent(event);" href="#">2</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(21);return Srch.U.cancelEvent(event);" href="#">3</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(31);return Srch.U.cancelEvent(event);" href="#">4</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(41);return Srch.U.cancelEvent(event);" href="#">5</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(51);return Srch.U.cancelEvent(event);" href="#">6</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(61);return Srch.U.cancelEvent(event);" href="#">7</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(71);return Srch.U.cancelEvent(event);" href="#">8</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(81);return Srch.U.cancelEvent(event);" href="#">9</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(91);return Srch.U.cancelEvent(event);" href="#">10</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(101);return Srch.U.cancelEvent(event);" href="#">11</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(111);return Srch.U.cancelEvent(event);" href="#">12</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(121);return Srch.U.cancelEvent(event);" href="#">13</a>
</div>
<div class="sas-paging-tablecell sas-paging-currentpage">14</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(141);return Srch.U.cancelEvent(event);" href="#">15</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(151);return Srch.U.cancelEvent(event);" href="#">16</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(161);return Srch.U.cancelEvent(event);" href="#">17</a>
</div>
<div class="sas-paging-tablecell">
<a onclick="$getClientControl(this).page(171);return Srch.U.cancelEvent(event);" href="#">18</a>
</div>
<div class="sas-paging-tablecell sas-paging-summary">Page
<span>14</span>of
<span>18</span>, items
<span>131</span>to
<span>140</span>of
<span>179</span>.</div>
</div>
</div>
</div>
The Control template (SwissArmy_Control_Template.js) creates the top level <div> and a child <ul> element (with a CSS class of “sas-table”) for the overall HTML structure, as well as the individual <li> elements used to contain each search/result item. For each search result/item, the Item template (SwissArmy_Item_Template.js) creates a <div> block that’s inserted as a child within an <li> element. Each of the individual item <div> blocks created by the Item template has a CSS class of “sas-tablecell” for easier styling.
Below the <ul> block housing the search results is another <div> with a CSS class of “sas-paging-table.” As suggested by the class name, the <div> is used to house the clickable page numbers and additional paging information seen at the bottom of the CSWP control. And, like the rest of the container information, this HTML is generated within the Control template.
Each time a set of search results is needed, either on initial page rendering or when the user moves to a new page, the CSWP calls back to its SharePoint site collection for the data it needs. By default, this call occurs asynchronously. However, the CSWP can be configured to make such calls synchronously within the normal page processing sequence.
For example, if I had a page containing a CSWP at the following URL:
http://sp2013-dev:18580/DisplayTemplateStyling
the CSWP on that page would request search results for display by posting a request to the following endpoint:
http://sp2013-dev:18580/_vti_bin/client.svc/ProcessQuery
As part of its request, the CSWP passes an XML structure to the client.svc web service that looks something like the XML that appears below. This structure contains all of the information the ProcessQuery method needs to determine which search results should be sent back:
<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="15.0.0.0" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0" />
<SetProperty Id="2" ObjectPathId="0" Name="QueryTemplate">
<Parameter Type="String">(contentclass:STS_ListItem OR IsDocument:True)</Parameter>
</SetProperty>
<ObjectPath Id="4" ObjectPathId="3" />
<Method Name="Add" Id="5" ObjectPathId="3">
<Parameters>
<Parameter Type="String">LastModifiedTime</Parameter>
<Parameter Type="Number">1</Parameter>
</Parameters>
</Method>
<SetProperty Id="6" ObjectPathId="0" Name="StartRow">
<Parameter Type="Number">130</Parameter>
</SetProperty>
<SetProperty Id="7" ObjectPathId="0" Name="RowsPerPage">
<Parameter Type="Number">10</Parameter>
</SetProperty>
<SetProperty Id="8" ObjectPathId="0" Name="RowLimit">
<Parameter Type="Number">10</Parameter>
</SetProperty>
<SetProperty Id="9" ObjectPathId="0" Name="TotalRowsExactMinimum">
<Parameter Type="Number">141</Parameter>
</SetProperty>
<SetProperty Id="10" ObjectPathId="0" Name="SourceId">
<Parameter Type="Guid">{8413cd39-2156-4e00-b54d-11efd9abdb89}</Parameter>
</SetProperty>
<ObjectPath Id="12" ObjectPathId="11" />
<Method Name="SetQueryPropertyValue" Id="13" ObjectPathId="11">
<Parameters>
<Parameter Type="String">SourceName</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">Local SharePoint Results</Property>
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="14" ObjectPathId="11">
<Parameters>
<Parameter Type="String">SourceLevel</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">Ssa</Property>
</Parameter>
</Parameters>
</Method>
<ObjectPath Id="16" ObjectPathId="15" />
<Method Name="Add" Id="17" ObjectPathId="15">
<Parameters>
<Parameter Type="String">Path</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="18" ObjectPathId="15">
<Parameters>
<Parameter Type="String">Title</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="19" ObjectPathId="15">
<Parameters>
<Parameter Type="String"> FileExtension</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="20" ObjectPathId="15">
<Parameters>
<Parameter Type="String"> SecondaryFileExtension </Parameter>
</Parameters>
</Method>
<ObjectPath Id="22" ObjectPathId="21" />
<Method Name="Add" Id="23" ObjectPathId="21">
<Parameters>
<Parameter Type="String">Title</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="24" ObjectPathId="21">
<Parameters>
<Parameter Type="String">Path</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="25" ObjectPathId="21">
<Parameters>
<Parameter Type="String">Author</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="26" ObjectPathId="21">
<Parameters>
<Parameter Type="String">SectionNames</Parameter>
</Parameters>
</Method>
<Method Name="Add" Id="27" ObjectPathId="21">
<Parameters>
<Parameter Type="String">SiteDescription</Parameter>
</Parameters>
</Method>
<SetProperty Id="28" ObjectPathId="0" Name="TrimDuplicates">
<Parameter Type="Boolean">false</Parameter>
</SetProperty>
<Method Name="SetQueryPropertyValue" Id="29" ObjectPathId="11">
<Parameters>
<Parameter Type="String">TryCache</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">true</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">3</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="Null" />
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="30" ObjectPathId="11">
<Parameters>
<Parameter Type="String">Scope</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">{Site.URL}</Property>
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="31" ObjectPathId="11">
<Parameters>
<Parameter Type="String">UpdateLinksForCatalogItems</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">true</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">3</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="Null" />
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="32" ObjectPathId="11">
<Parameters>
<Parameter Type="String">EnableStacking</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">true</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">3</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="Null" />
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="33" ObjectPathId="11">
<Parameters>
<Parameter Type="String">ListId</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">fb9e18d3-1d80-4ab1-8e76-bc36b0a8e22d</Property>
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="34" ObjectPathId="11">
<Parameters>
<Parameter Type="String">ListItemId</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">6</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">2</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="Null" />
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="35" ObjectPathId="11">
<Parameters>
<Parameter Type="String">TermId</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">0bfc89ed-1741-4925-9d25-828eaf74b2c8</Property>
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="36" ObjectPathId="11">
<Parameters>
<Parameter Type="String">TermSetId</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">0f14334e-b929-42bf-ad10-ed6a93d16a4f</Property>
</Parameter>
</Parameters>
</Method>
<Method Name="SetQueryPropertyValue" Id="37" ObjectPathId="11">
<Parameters>
<Parameter Type="String">TermStoreId</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">c4c58328-0e88-4937-91e9-cf7526b9b0db</Property>
</Parameter>
</Parameters>
</Method>
<SetProperty Id="38" ObjectPathId="0" Name="ResultsUrl">
<Parameter Type="String">http://sp2013-dev:18580/DisplayTemplateStyling#k=#s=131</Parameter>
</SetProperty>
<SetProperty Id="39" ObjectPathId="0" Name="BypassResultTypes">
<Parameter Type="Boolean">true</Parameter>
</SetProperty>
<SetProperty Id="40" ObjectPathId="0" Name="ClientType">
<Parameter Type="String">ContentSearchRegular</Parameter>
</SetProperty>
<SetProperty Id="41" ObjectPathId="0" Name="EnableInterleaving">
<Parameter Type="Boolean">false</Parameter>
</SetProperty>
<SetProperty Id="42" ObjectPathId="0" Name="ProcessBestBets">
<Parameter Type="Boolean">false</Parameter>
</SetProperty>
<Method Name="SetQueryPropertyValue" Id="43" ObjectPathId="11">
<Parameters>
<Parameter Type="String">QuerySession</Parameter>
<Parameter TypeId="{b25ba502-71d7-4ae4-a701-4ca2fb1223be}">
<Property Name="BoolVal" Type="Boolean">false</Property>
<Property Name="IntVal" Type="Number">0</Property>
<Property Name="QueryPropertyValueTypeIndex" Type="Number">1</Property>
<Property Name="StrArray" Type="Null" />
<Property Name="StrVal" Type="String">a4e0e9b5-6dd9-4aea-b38b-26f325c67fdf</Property>
</Parameter>
</Parameters>
</Method>
<SetProperty Id="44" ObjectPathId="0" Name="ProcessPersonalFavorites">
<Parameter Type="Boolean">false</Parameter>
</SetProperty>
<SetProperty Id="45" ObjectPathId="0" Name="SafeQueryPropertiesTemplateUrl">
<Parameter Type="String">querygroup://webroot/Pages/DisplayTemplateStyling.aspx?groupname=Default</Parameter>
</SetProperty>
<SetProperty Id="46" ObjectPathId="0" Name="IgnoreSafeQueryPropertiesTemplateUrl">
<Parameter Type="Boolean">false</Parameter>
</SetProperty>
<ObjectPath Id="48" ObjectPathId="47" />
<ExceptionHandlingScope Id="49">
<TryScope Id="51">
<Method Name="ExecuteQueries" Id="53" ObjectPathId="47">
<Parameters>
<Parameter Type="Array">
<Object Type="String">26103082-0e16-4ab0-a12c-bded636a7fb8Default</Object>
</Parameter>
<Parameter Type="Array">
<Object ObjectPathId="0" />
</Parameter>
<Parameter Type="Boolean">true</Parameter>
</Parameters>
</Method>
</TryScope>
<CatchScope Id="55" />
</ExceptionHandlingScope>
</Actions>
<ObjectPaths>
<Constructor Id="0" TypeId="{80173281-fffd-47b6-9a49-312e06ff8428}" />
<Property Id="3" ParentId="0" Name="SortList" />
<Property Id="11" ParentId="0" Name="Properties" />
<Property Id="15" ParentId="0" Name="SelectProperties" />
<Property Id="21" ParentId="0" Name="HitHighlightedProperties" />
<Constructor Id="47" TypeId="{8d2ac302-db2f-46fe-9015-872b35f15098}" />
</ObjectPaths>
</Request>
Once the server finds the desired search results and processes them, it packages them as a JSON object and returns that object to the browser for further action. The following is an example of a JSON structure (containing 10 results) that was returned for the Swiss Army Knife CSWP as it was shown earlier in the “Better Paging Support” section:
function DisplayTemplate_2aa45a743fd94e75a3e53c940628e2ec(ctx) { ... }
This JSON structure contains a lot of information — certainly more than is being displayed by the CSWP. The trick, of course, is in figuring out exactly “what” is “where” for purposes of building a paging system.
Thankfully, the CSWP provides us with a relatively easy mechanism for getting at the search result data we care about within the JSON object that’s returned from the call to client.svc. When a Control display template is invoked by the CSWP, it’s passed a context object as follows:
undefined
The ctx context object contains a number of useful methods, properties and subordinate objects we can leverage in our attempts to manipulate search results and calculate paging information. We can use ctx to interact directly with the CSWP (which is accessible through the ClientControl property), as well as obtain the search results themselves (and information about them) through the ListData property.
Note: Even though Microsoft’s List with Paging Control template doesn’t provide anything more than relative paging forward and backward, the CSWP does appear to support some form of more advanced paging scheme through its get_pagingInfo() method. When this method is called, it returns an object that contains expanded paging information, including information about the current page, as well as a subset of pages before and after the current page. The object does not, however, contain all of the information needed to determine the total number of items in the search result set, how to access those pages, etc.
Fortunately for us, though, the ctx.ListData object contains everything we need to implement an end-to-end paging system. Here’s how each of the critical paging information pieces is located or computed within the Control template so that the portion of the control seen below can be rendered:
Total number of all search results available — The totalRowCount variable is used within the Control display template scripting to store this value. And the value itself is readily available through the ctx.ListData.ResultTables[0].TotalRows property.
In addition to the values described above, a series of calculations are performed to determine the currentPageStartItem and currentPageEndItem variables that represent the first and last item numbers on the current page. Once these values are known and assigned to their respective variables, it becomes a relatively straightforward exercise to display the paging information as is done within the SwissArmy_Control_Template.js Control template. The (not-so-heavy) lifting is accomplished with the following JavaScript:
undefined
One area does warrant a little more explanation, though, and that’s how the hyperlinks are created for each of the clickable page numbers at the bottom of the CSWP. There really isn’t a whole lot of magic behind the creation of the hyperlinks themselves. The results are achieved on line 101 of the SwissArmy_Control_Template.js file:
undefined
The pageStartItem variable value is computed as follows …
pageStartItem = ((i - 1) * rowsPerPageCount) + 1;
… where i is the value of the new result page being clicked by the user. For example, if the user clicks on “17” (to indicate a desire to move to page 17 of the search results), the value of i will be 161 when there are 10 items per page.
http://sp2013-dev:18580/DisplayTemplateStyling#k=#s=161
On the server side, SharePoint translates the request for 161 (which can also be reached directly via repost by attaching a #s=161 to the query string portion of the URL as shown above) to package and send down a search results table containing all of the page 17 result items.
The two HTML files are each of the display templates in their HTML form. These HTML files can be processed through the SharePoint 2013 Design Manager to generate the corresponding JavaScript files actually used by the CSWP. Alternatively, the JavaScript files included in the zip file can be used as is and dropped directly into the Master Page Gallery > Display Templates > Content Web Parts folder within a site collection to make them available to the CSWP.
The remaining file is a CSS style sheet, and it provides the look and feel employed by the display templates. The style sheet itself is referenced from with the Control template files (SwissArmy_Control_Template.html and SwissArmy_Control_Template.js), and the Control template files assume the style sheet will be available in the following location within the site collection:
~sitecollection/Style Library/en-us/SwissArmyStyles/SwissArmy_Styles.css
If you place the style sheet somewhere else in the site collection, ensure the $includeCSS() calls in the SwissArmy_Control_Template.html file (line 25) and the SwissArmy_Control_Template.js file (line 164) are updated accordingly.
As with all of the resources I make available, feel free to use the display templates and style sheet I’ve provided within your own projects — either as is or in a form you’ve modified to suit your needs.
If you do share or redistribute what I’ve provided in some form, whether or not you’ve made modifications, I simply ask that you reference the original source (this article) in what you’re sharing. Thanks, and have fun paging through search results.
This article originally appeared on May 28, 2015.