Today I had to modify an existing SharePoint 2013 Content By Search Web Part that used a custom display template to show more than 50 search results without paging!
I came across a blogpost from Scott Ewing describing the different options. Basically you have 3 different options in an on-premise environment:
- Attempt a client-side approach to override the 50 result limit: We tried this approach but it didn’t work in an anonymous scenario.
- Try to override the behavior of the ContentBySearchWebPart on the server-side, possibly using reflection if necessary: We didn’t like the fact to override this behavior. Also when a user changes the webpart properties you will end up with an ugly error message.
- Figure out how to use a jQuery infinite scrolling approach with SharePoint search results: Yes, this is the way to go, but can we do it without the scrolling part? Can we immediately show more than 50 results? Of course … read on …
The infinite retrieving of items was also described in a blogpost by Elio Struyf, only it seamed not to work when refreshing the page. So I combined the approach found in a reply by Anvil DSouza. Another small issue with the sample from Elio Struyf seamed to be that it didn’t work when search results were less than 50 items. Also that little bug is solved by the below code.
Enough chitchat, let’s get to the code. A lot of inline comments are added to explain all steps.
<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"> <head> <title>Page Control Display Template</title> <!--[if gte mso 9]><xml> <mso:CustomDocumentProperties> <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden> <mso:MasterPageDescription msdt:dt="string">This is the default Control Display Template that will list the items. It does not allow the user to page through items.</mso:MasterPageDescription> <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106601</mso:ContentTypeId> <mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType> <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated> <mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded> <mso:HtmlDesignStatusAndPreview msdt:dt="string">DisplayTemplateMoreThan50Results.html, conversion has completed.</mso:HtmlDesignStatusAndPreview> <mso:FeatureId msdt:dt="string">d352e976-07fc-4529-b0a5-ddb79b139bc8</mso:FeatureId> </mso:CustomDocumentProperties> </xml><![endif]--> </head> <body> <script> $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js"); </script> <div id="Control_List"> <!--#_ if (!$isNull(ctx.ClientControl) && !$isNull(ctx.ClientControl.shouldRenderControl) && !ctx.ClientControl.shouldRenderControl()) { return ""; } ctx.ListDataJSONGroupsKey = "ResultTables"; var $noResults = Srch.ContentBySearch.getControlTemplateEncodedNoResultsMessage(ctx.ClientControl); var noResultsClassName = "ms-srch-result-noResults"; /* Get the Hidden Element ID defined below (in html) where our batches of 50 search results will be filled. */ var hiddenElmId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_Results_"); /* On PostRender of the initial results we will call a new function which will fetch the next ones. All functionality in this inline function will be done each time a batch of 50 results is retrieved. */ ctx.OnPostRender = []; ctx.OnPostRender.push(function () { /* All steps described in this code sample can also be fully read at following sources: Start point describing different options: http://www.scottewing.net/Blog/Post/2/Getting-Around-the-Content-Search-Web-Part%27s-50-Results-Limit Nice step by step guide with a more-results link button by Elio Struyf: http://www.eliostruyf.com/create-a-load-more-results-link-button-for-the-content-search-web-part/ Doesn't work good with page refreshes (and less than 50 items), so also implemented following approach: http://sharepoint-community.net/forum/topics/how-to-override-content-by-search-web-part-s-limit-of-50-results */ /* A Session Cookie Value, containing a simple flag to detect page refreshes. */ var IsRefresh = sessionStorage.IsRefresh; /* Hide the original set of results, this container only holds 50 items. */ var hiddenElm = $('#' + hiddenElmId); hiddenElm.hide(); /* Create a new Visible container where the search results will be copied to. This element has to be placed OUTSIDE of the render area the Control Display Template. (Outside of the WebPart Body) */ if ($('#someUniqueDivId').length<=0){ hiddenElm.parents('.ms-WPBody:eq(0)').before('<div id="someUniqueDivId"></div>'); } var visibleElm = $('#showVisibileItems'); if (visibleElm.length<=0){ // Visible element was not yet created, so create it in the DOM & get it again. // Get the tag name of the element var tagname = hiddenElm.prop('tagName'); $('#someUniqueDivId').append('<' + tagname + ' id="showVisibileItems" class="' + hiddenElm.attr('class') + '"></' + tagname + '>'); visibleElm = $('#showVisibileItems'); } /* This logic is only executed on Page Refreshes. It will clear the divs and reset the Search Results to Page 1 */ if (IsRefresh != null && IsRefresh != "") { //Refreshed Page detected ... //cookie exists then you refreshed this page(F5, reload button or right click and reload) visibleElm.empty(); hiddenElm.empty(); sessionStorage.IsRefresh = ""; //Revert flag again. ctx.ClientControl.page(1); } /* Append (MOVE) all the hidden items to the visible items element */ hiddenElm.children().each(function () { $(this).appendTo(visibleElm); }); /* Refresh logic has been handled above. Following logic is only done on fresh pageloads. */ if (!IsRefresh) { /* Get the paging information from the current Context */ var pagingInfo = ctx.ClientControl.get_pagingInfo(); var lastPage = pagingInfo[pagingInfo.length -1]; if (pagingInfo && pagingInfo.length) { /* Means that we have more than 50 items In a default Display template we would get paging ... */ if(lastPage.startItem<0){ /* Last Results are Loaded... You can add some extra logic here that should happen when everything is shown. */ hiddenElm.empty(); sessionStorage.IsRefresh = "True"; } else{ /* More Results to be shown, so load them in hidden element again. This line is essential and will trigger the OnPostRender function again! */ ctx.ClientControl.page(lastPage.startItem); } } else { /* Means that we have no paging, less than 50 items are returned As a safety measure, clean out the hidden Element, and store the flag. */ hiddenElm.empty(); sessionStorage.IsRefresh = "True"; } } }); _#--> <ul id="_#=hiddenElmId=#_" class="cbs-List"> _#= ctx.RenderGroups(ctx) =#_ </ul> <!--#_ if (ctx.ClientControl.get_shouldShowNoResultMessage()) { _#--> <div class="_#= noResultsClassName =#_">_#= $noResults =#_</div> <!--#_ } _#--> </div> </body> </html>
Sources:
- Blogpost Scott Ewing: http://www.scottewing.net/Blog/Post/2/Getting-Around-the-Content-Search-Web-Part%27s-50-Results-Limit
- Blogpost Elio Struyf: http://www.eliostruyf.com/create-a-load-more-results-link-button-for-the-content-search-web-part
- Interesting reply by Anvil DSouza: http://sharepoint-community.net/forum/topics/how-to-override-content-by-search-web-part-s-limit-of-50-results