jsLink: how to display a custom ‘no items’ message




If you use jsLink to override the rendering of list views then you may have noticed that your custom jsLink no longer renders a message when there are no items returned in the view. I am going to discuss with code samples how to display a ‘no items’ message – or at least help you stop overriding it.

You have complete control over how list items are rendered using jsLink
You have complete control over how list items are rendered using jsLink

If, alternatively, you have a ‘no items’ message being displayed and just want to modify the text, try this link.

If you don’t know what jsLink is then it is worth learning about it. Try this link.

What am I doing wrong?

Chances you are making the same mistake that many people make. A mistake that has been replicated again and again online and doesn’t break anything but does prevent the display of the ‘no items’ message and the paging control. When you override Templates.Header you DO NOT need to override Templates.Footer in order to close tags which you opened in the header.

(function () {
    var overrideCtx = {};
    overrideCtx.Templates = {};

    overrideCtx.Templates.Header = "<div id=\"my-thing\">";
    overrideCtx.Templates.Item = MyItemTemplateFunction;
    
    // There is no need to override this member. 
    // By doing so you are also overriding the no items message
    // as well as the paging control.
    // Elements in the header template are closed for you.
    //overrideCtx.Templates.Footer = "</div>";

    overrideCtx.BaseViewID = 1;
    overrideCtx.ListTemplateType = 101;

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

Although doing so seems to make sense, you can rest assured knowing that tags you open in the header will be closed auto-magically after the item templates have completed rendering. In fact, the footer template is rendered in a different table cell to the header and item templates when this all hits the page. Think of the footer template as a distinct block that is rendered after everything else rather than the end of the same block.

By overriding the footer template you are also inadvertently overriding the ‘no items’ message and the list view paging control. You can see exactly what you are overriding by inspected the default values for the templates. Below is snippet from clientrenderer.js which shows the default footer template.

RenderFooterTemplate = function(renderCtx) {
	var ret = [];

	RenderEmptyText(ret, renderCtx);
	RenderPaging(ret, renderCtx);
	return ret.join('');
};
	
function RenderEmptyText(ret, renderCtx) {
    if (renderCtx.inGridMode) {
        return;
    }
    var listData = renderCtx.ListData;

    if (listData.Row.length == 0) {
        var listSchema = renderCtx.ListSchema;
        var iStr = '<table class="';
        var hasSearchTerm = typeof renderCtx.completedSearchTerm != "undefined" && renderCtx.completedSearchTerm != null;

        iStr += 'ms-list-emptyText-compact ms-textLarge';
        iStr += '" dir="';
        iStr += listSchema.Direction;
        iStr += '" border="0">';
        iStr += '<tr id="empty-';
        iStr += renderCtx.wpq;
        iStr += '"><td colspan="99">';
        var listTemplate = renderCtx.ListTemplateType;

        if (hasSearchTerm) {
            iStr += Strings.STS.L_NODOCSEARCH;
        }
        else if (listSchema.IsDocLib) {
            var viewTitle = renderCtx.viewTitle;

            if (Boolean(viewTitle))
                iStr += Strings.STS.L_NODOC.replace("%0", STSHtmlEncode(viewTitle));
            else
                iStr += Strings.STS.L_NODOCView;
        }
        else if (listTemplate == 160) {
            iStr += Strings.STS.L_AccRqEmptyView;
        }
        else {
            iStr += STSHtmlEncode(listSchema.NoListItem);
        }
        iStr += '</td></tr></table>';
        ret.push(iStr);
    }
}

function RenderPaging(ret, renderCtx) {
 // Excluded for brevity
}

So what should you do?

If you just want the default no items message and can get away with not overriding the footer template (as in the first code snippet), then great – you are all done.

If want a custom message then check out the link at the very top of the article (in summary: renderCtx.ListSchema.NoListItem = "Nada, nothing, zilch";).

If you want to override the footer template or perhaps you want the message to appear within a wrapper tag defined in the header or you want some custom logic behind which message to display then you can do that too – keep reading.

Doing it yourself

I’ve written a utility function that is based on the logic in the OOTB footer template that makes it easier to manage the ‘no items’ text. This function does NOT replicate the paging functionality. If you need paging and are overriding the footer template then you will need to replicate the paging functionality as well. You will need to look into clientrenderer.js to find out how MSFT do this.
Looking at this snippet you can see the if-else block where you can define custom messages for different list templates or if the lack of results has occurred only after a search term was provided. This sample should not be considered the superlative version, it just does a basic job in line with what happens by default.

var jsLinkGetNoItemsHtmlObj = function (renderCtx, forceMessage) {
    // Create the object to return with default values- we will update this before it is returned
    var resultObj = {
        noItemsText: "No render context provided",
        noItemsHtml: "No render context provided",
        isNoItems: true
    };

    if (renderCtx) {
        // Determine if there are any items to render or not
        resultObj.isNoItems = !renderCtx.inGridMode && renderCtx.ListData.Row.length === 0;
        // Set the message to the provided message if there is one
        if (typeof forceMessage === "string" && forceMessage.length > 0) {
            resultObj.noItemsText = forceMessage;
        }
        else {
            // If an 'override' message is not provided then use the OOTB logic to determine the text
            // NOTE: the below is logically pulled from clientrenderer.js, I have just refactored it a bit
            var hasSearchTerm = typeof renderCtx.completedSearchTerm === "string" && renderCtx.completedSearchTerm.length > 0 ;
            var isDocLib = renderCtx.ListSchema.IsDocLib;
            var isAccessRequestList = renderCtx.ListTemplateType === 160;

            if (hasSearchTerm) {
                resultObj.noItemsText = Strings.STS.L_NODOCSEARCH;
            }
            else if (isDocLib) {
                var viewTitle = renderCtx.viewTitle;
                if (typeof viewTitle === "string" && viewTitle.length > 0 {
                    resultObj.noItemsText = Strings.STS.L_NODOC.replace("%0", STSHtmlEncode(viewTitle));
                }
                else {
                    resultObj.noItemsText = Strings.STS.L_NODOCView;
                }
            }
            else if (isAccessRequestList) {
                resultObj.noItemsText = Strings.STS.L_AccRqEmptyView;
            }
            else {
                resultObj.noItemsText = STSHtmlEncode(renderCtx.ListSchema.NoListItem);
            }
        }
    }
    // Wrap the text in a span to ensure that this text can be easily rendered consistently.
    resultObj.noItemsHtml = "<span class='ms-list-emptyText-compact ms-textLarge'>" + resultObj.noItemsText + "</span>";
    return resultObj;
};

Below are two examples of how you may want to use this. The first is by overriding the footer template, and the second is by overriding the header template. The advantage of sticking this code into the header template is that it allows you wrap the no items message in the same wrapper tags that you defined for the main content.

// Example footer template that renders the 'no items' message when required.
// Note that this paging will not be rendered unless explicity included in the footer template
var footerTemplate = function () {
    var footerContentHtml = "";
    var noItemsObj = jsLinkGetNoItemsHtmlObj(renderCtx, "Key documents are yet to be selected by the site administrator");
    if (noItemsObj.isNoItems) {
        footerContentHtml = noItemsObj.noItemsHtml;
    }
    return footerContentHtml;
};

// Example header template that is intended to wrap an unorded list.
// Note that the UL is only rendered when there are items available to render.
// This approach enables the 'no items' message to be rendered inside the wrapper
// tag which is defined in header.
// NOTE 2 - you must override the footer template as well or you'll get double
// messages
var headerTemplate = function (renderCtx) {
    var headerContentHtml = "<ul>";
    var noItemsObj = jsLinkGetNoItemsHtmlObj(renderCtx, "Key documents are yet to be selected by the site administrator");
    if (noItemsObj.isNoItems) {
        headerContentHtml = noItemsObj.noItemsHtml;
    }
    return "<div class='project-documents project-panel'>" + headerContentHtml;
};

Paul.

For aiding findability:

  • There are no items to show in this view of the list
  • Your search returned no results
  • Some items might be hidden. Include these in your search
  • Still didn’t find it? Try searching the entire site.




Leave a Reply

Your email address will not be published.