Wednesday, February 15, 2017

Sharepoint 2013 CSR ajax paging issue

There is a common SharePoint 2013 CSR pagination issue :
if you override Header/Footer/Item template it will break ajax pagination –
and after paginator button is clicked the whole page will load.

After spending a lot of time finding standard way of fixing this I found a solution without of using Client Object model and jquery pagination and based on this article.

1. If you need override footer template you should use ctx.ListData.NextHref and ctx.ListData.PrevHref to render html like this:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
    Templates: {
      Footer: function (ctx) {
        var footerHtml = '<table class="ms-bottompaging"><tr>';
        var next = ctx.ListData.NextHref;
        var prev = ctx.ListData.PrevHref;

        footerHtml += "<td><a href='javascript:void(0);' onclick=\"RefreshPageTo(event, &quot;" + prev + "&quot;);return false;\"><span><img class='ms-promlink-button-left' src='/_layouts/15/images/spcommon.png?rev=23'/></span></a></td>";

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

        footerHtml += "</tr></table>";
        return footerHtml;
      }
    }});

But in most cases I do not advise override footer - OOTB footer template is  good.

2. If you override the header template, you should do the next trick. 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. You need generate it and insert into rendering html code. There is standard core function RenderTableHeader (clienttemplates.js) , but you need only last part of generated by RenderTableHeader code:

overrideContext.Templates.Header = function (ctx) {
        var headerHtml = RenderTableHeader(ctx) + "</table>";
        headerHtml = headerHtml.substring(headerHtml.indexOf("<table onmousedown") - 9);
        headerHtml += "<div class='news-main'>News: </div><div class='rh-rcol-button'>" +
        "<a href='" + _spPageContextInfo.webServerRelativeUrl + _constants.GlobalNews.Url + _constants.GlobalNews.Views.Filtered + "'>All news <img src='~site/_layouts/15/Comp/EAE.LUK.GR/img/icon_go.png'/></a></div><br>";
        return headerHtml;
    };

3. If you override the item template, there are two cases:
а) you are rendering new rows of html table;
In this case if you table is standard – you don’t need close you table in header template  after calling RenderTableHeader(ctx). But if your need to apply custom css class to table you HAVE TO CLOSE 'ms-listviewtable' OOTB TABLE AND CREATE NEW TABLE with custom css class in header template, because if you just apply custom class to  OOTB TABLE  paging goes non-Ajax yet again.

b) you are rendering new rows by different html elements(div, etc.)
In this case there is another problem - Ajax paging will working but after page number is changed item area will be empty... The trick is to append them by hands:

overrideContext.Templates.Item = function (ctx) {
        var html =
        "<div class='listview-date-field'>" ….."</div>";
        // check whether the Ajax request is (found empirically)
        if (ctx.operationType > 0) {
            var startElement = $("#scriptBody" + ctx.wpq);
            if (startElement.nextAll('div').length > 0) {
                startElement = startElement.nextAll('div:last');
            }
            startElement.after(html);
        }
        return html;
    };

The code $("#scriptBody" + ctx.wpq);  is for the case of multiple web-parts on the page.


Sometimes,  this code will not working and  page listing breakes. The cause is different order of loading javascript files and clvp object of 'ms-listviewtable' OOTB TABLE is undefined.

To fix it you need to call  InitAllClvps() in  OnPostRender  event handler:
overrideContext.OnPostRender =  function (ctx) {
        fixContext();     
    };

function fixContext() {
        var ctx = window.ctx;
        if (ctx) {
            ctxLocal = window["ctx" + ctx.ctxId];
            if (!ctxLocal || !ctxLocal.clvp) {
                EnsureScript("inplview", typeof InitAllClvps, function () {
                    if (ctxLocal.clvp == null)
                        InitAllClvps();
                });
            }
        }

    }