2

So I followed this post to implement paging in my custom JSLINK rendering template, and got that working great. The only downside to it is that the paging actions are full page post-backs whereas on the stock SP list web-part they use ajax. After digging into the the MS code I found that the paging calls were being wrapped with a call to the RefreshPageTo, after employing this I'm still not getting ajax paging.

In the MS script INPLVIEW.js their seems to be some rules in how the markup is decorated and a property they add somewhere called clvp to one of the outer rendering block html elements. It seems their must be a JS method that I can call to ajaxify my template markup block somehow.

My question is how can ajax paging be implemented in a custom JSLINK rendering template? This simple template below should work with Ajax paging but does not.

window.MyNewsList = window.MyNewsList || {};
window.MyNewsList.NewsLink = {
    header: function (ctx) {
        var hdrHtml = "";
        if( window.MyNewsList.NewsLink.canAddNews() ) {
            hdrHtml += "<div>";
                hdrHtml += "<span class=\"link-item\"><a href=\"/news/Lists/Pages/Forms/NewForm.aspx\">Add New</a></span>";
            hdrHtml += "</div>";
        }
        return hdrHtml;
    },

    body: function (ctx) {
        bodyHtml = "";
        bodyHtml += "<div>";
        bodyHtml += ctx.CurrentItem.Title;
        bodyHtml += "</div>";
        return bodyHtml;
    },
    footer: function (ctx) {
        var footerHtml = "";
        var firstRow = ctx.ListData.FirstRow;
        var lastRow = ctx.ListData.LastRow;
        var prev = ctx.ListData.PrevHref;
        var next = ctx.ListData.NextHref;

           footerHtml += "<table class=\"ms-bottompaging\"><tr><td>";
            if (prev)
                footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-enabled' href='" + prev + "'><span class='ms-promlink-button-image'><img class='ms-promlink-button-left' src='/_layouts/15/images/spcommon.png?rev=23' /></span></a>";
            else
                footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-disabled' href='javascript:void(0);'><span class='ms-promlink-button-image'><img class='ms-promlink-button-left-disabled' src='/_layouts/15/images/spcommon.png?rev=23' /></span></a>";

            footerHtml += "<span class=\"ms-promlink-button-inner\"></span>";

            if (next)
                footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-enabled' href='javascript:void(0);' onclick=\"RefreshPageTo(event, &quot;" + next + "&quot;);return false;\"><span class='ms-promlink-button-image'><img class='ms-promlink-button-right' src='/_layouts/15/images/spcommon.png?rev=23'/></span></a>";
            else
                footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-disabled' href='javascript:void(0);'><span class='ms-promlink-button-image'><img class='ms-promlink-button-right-disabled' src='/_layouts/15/images/spcommon.png?rev=23' /></span></a>";

            footerHtml += "</td></tr></table>";

        return footerHtml;
    }
};

// anonymous self-executing function to setup JSLink templates on page load..
(function () {
    var overrideCtx = {};
    overrideCtx.Templates = {};

    //Tempate overrides
    overrideCtx.Templates.Header = window.MyNewsList.NewsLink.header;
    overrideCtx.Templates.Item = window.MyNewsList.NewsLink.body;
    overrideCtx.Templates.Footer = window.MyNewsList.NewsLink.footer;

    //List Settings
    //Pages List has Type 850 publishing page library
    //BaseViewID = 1 means default View
    //overrideCtx.BaseViewID = 1;
    overrideCtx.ListTemplateType = 850;

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
})();

EDIT:

Unfortunately the accepted solution didn't work out after all. It would go to the first page fine but then if you went back to the prior page the template does not render your markup. From there it just gets really glitched up. I ended up just punting and leaving it with the postback paging until I have more time to dig into this.

4 Answers 4

14

SharePoint is a huge historical monster. Core of the current paging implementation was introduced long ago, probably in SP2010 or even earlier. That's why your approach doesn't work: SharePoint isn't finding some html elements that are supposed to be there, and falls back to a simpler approach with link.

Investigation

Here is why it happens (you may skip to solution if not interested).

Core of RefreshPageTo implementation is found in inplview.RefreshPageToEx function of inplview.js file:

function RefreshPageToEx(evt, url, bForceSubmit) {
    var clvp = CLVPFromEvent(evt);

    if (clvp != null && clvp.ctx.IsClientRendering) {
        clvp.RefreshPaging(url);
        clvp.ctx.queryString = url;
        if ((typeof clvp.ctx.operationType == "undefined" || clvp.ctx.operationType == SPListOperationType.Default) && Boolean(clvp.ctx.ListData)) {
            var fromPage = clvp.ctx.ListData.FirstRow - 1;
            var toPage = Number(GetUrlKeyValue("PageFirstRow", false, url));

            if (!isNaN(fromPage) && !isNaN(toPage) && fromPage != toPage)
                fromPage < toPage ? (clvp.ctx.operationType = SPListOperationType.PagingRight) : (clvp.ctx.operationType = SPListOperationType.PagingLeft);
        }
    }
    else {
        SubmitFormPost(url, bForceSubmit);
    }
}

As you can see, this implementation depends on clvp object, which basically represents the list view context, containing all the necessary information for paging and other list view tasks. CLVPFromEvent function call fetches the clvp object from the current event, by ascending from the event element (which is the <span> that is clicked) and trying to find TABLE with class 'ms-listviewtable'. That table contains attribute clvp that stores the desired clvp object.

If TABLE with class 'ms-bottompaging' is found, then the CLVPFromEvent function processes to the previous sibling of the parent of this table, and then finds 'ms-listviewtable' amongst it's children.

So the usual structure of a SharePoint list view is as follows:

<table>
  ...
  <table class="ms-listviewtable">...</table>
</table>
<div id="scriptPagingWPQ2">
  <table class="ms-bottompaging">
    ...
  </table>
</div>

But in case of custom rendering, you'll get the following structure:

<table>
  ...
  <table class="ms-listviewtable">...</table>
</table>
<div id="scriptPagingWPQ2">
  <!-- your custom footer code goes here -->
</div>

Since Ajax paging implementation is tightly depends on clvp object and can't find it, Ajax paging doesn't work.

Solution

Simplest approach would be of course to create this missing table. Like that:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
    Templates: {
      Footer: function (ctx) {
        var footerHtml = '<table class="ms-bottompaging"><tr><td>';
        var next = ctx.ListData.NextHref;
        footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-enabled' href='javascript:void(0);' onclick=\"RefreshPageTo(event, &quot;" + next + "&quot;);return false;\"><span class='ms-promlink-button-image'><img class='ms-promlink-button-right' src='/_layouts/15/images/spcommon.png?rev=23'/></span></a>";
        footerHtml += "</td></tr></table>";
        return footerHtml;
      }
    }
});

I tested it and it works.

Still, if you find any problems I might have missed, then it likely that some other "supposed" elements are missed there. In this case I would advise you to explore the original implementation in the OOTB function RenderPaging that can be found in the clienttemplates.js file in 15/template/layouts folder.

Although this approach works, but there might be cases when you want to avoid placing a table there due to some problems with CSS or maybe you're a purist and want to make things more perfect ;) In this case you might want to create your own implementation of the RefreshPageToEx function, so that it finds the necessary clvp object and calls clvp.RefreshPaging(url) directly. But this is more risky approach.

Update

It turns out that the 'ms-listviewtable' TABLE is generated by OOTB header template. If you override the header template, it is not generated and thus paging goes non-Ajax yet again. To fix that, you can call RenderTableHeader OOTB function and then just close the table tag.

So in case you're overriding the header template along with footer template, you should use the following fix:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
    Templates: {
      Header: function(ctx) {
        var headerHtml =  RenderTableHeader(renderCtx);
        headerHtml += "</table>";
        // add some other stuff to headerHtml
        return headerHtml;
      },
      Footer: function (ctx) {
        var footerHtml = '<table class="ms-bottompaging"><tr><td>';
        var next = ctx.ListData.NextHref;
        footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-enabled' href='javascript:void(0);' onclick=\"RefreshPageTo(event, &quot;" + next + "&quot;);return false;\"><span class='ms-promlink-button-image'><img class='ms-promlink-button-right' src='/_layouts/15/images/spcommon.png?rev=23'/></span></a>";
        footerHtml += "</td></tr></table>";
        return footerHtml;
      }
    }
});

So in your case you should change the header function as follows:

header: function (ctx) {
    var hdrHtml = RenderTableHeader(renderCtx) + "</table>";
    if( window.MyNewsList.NewsLink.canAddNews() ) {
        hdrHtml += "<div>";
            hdrHtml += "<span class=\"link-item\"><a href=\"/news/Lists/Pages/Forms/NewForm.aspx\">Add New</a></span>";
        hdrHtml += "</div>";
    }
    return hdrHtml;
},
15
  • +1 nice answer, I had tried wrapping it with that class="ms-bottompaging" table and it didn't seem work. Perhaps I over complicated it but I'll try it again with your markup.
    – James
    Sep 13, 2013 at 19:02
  • Could you please post your complete template code file that is working for you?
    – James
    Sep 13, 2013 at 19:05
  • Well the template is in the answer. I just put this code in CEWP above the list view web part, and saved the page, that's that. Sep 13, 2013 at 19:19
  • Thanks, I saw that, but I don't see what you're rendering besides page links, looking for header, body and RegisterTemplateOverrides block too.
    – James
    Sep 13, 2013 at 19:24
  • RegisterTemplateOverrides block is there. Please see the code block under "Solution" header in the answer. I'm not rendering header or body, only footer. That's all I have on the page, really. It only renders "Next" button, not previous, but it was quite enough for testing purposes. Sep 13, 2013 at 19:26
1

RefreshPageToEx is more powerful function and if table class is "ms-bottompaging" then no full refresh happens. if the class is reptetive or not being used then full postback happens. if try it and let me know if you still face the problem.

its bit late post but will update complete plugin sooner

1

Using the function "ctx.clvp.RefreshPagingEx(ctx.ListData.NextHref, true,null)" for the paging button onclick works great and avoids the need for extra markup and the entire JSLink chain is called.

0

Complete code:

var ClientsideContext = {};
ClientsideContext.Templates.Footer = loadFooter;

function loadFooter(ctx) {
    var footerHtml = '<table class="ms-bottompaging"><tr><td>';
    var firstRow = ctx.ListData.FirstRow;
    var lastRow = ctx.ListData.LastRow;
    var prev = ctx.ListData.PrevHref;
    var next = ctx.ListData.NextHref;
    if (prev) {
        footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-enabled' href='javascript:void(0);' onclick=\"RefreshPageTo(event, &quot;" + prev +
            "&quot;);return false;\"><span class='ms-promlink-button-image'><img class='ms-promlink-button-left' src='/_layouts/15/images/spcommon.png?rev=23'/></span></a>";
    }
    if (prev || next) {
        footerHtml += "<span class='ms-paging'><span class='First'>" + firstRow + "</span> - <span class='Last'>" + lastRow + "</span></span>";
}
    if (next) {
        footerHtml += "<a class='ms-commandLink ms-promlink-button ms-promlink-button-enabled' href='javascript:void(0);' onclick=\"RefreshPageTo(event, &quot;" + next +
            "&quot;);return false;\"><span class='ms-promlink-button-image'><img class='ms-promlink-button-right' src='/_layouts/15/images/spcommon.png?rev=23'/></span></a>";
}
    footerHtml += "</td></tr></table>";
    return footerHtml;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.