Friday, February 14, 2014

Show announcements the old fashioned way replacing XSLT with JS Link

In my two previous posts (Sample on how to use Content Search to display an announcement list and Creating a custom display template for a content search web part) I showed how to use Content Search in order to bring filtered items back from an announcement list and formatting the result with custom html via display templates.

Topics covered in this post
  • XsltListView web part using JS Link
  • Referencing multiple .js files with JS Link
  • Custom date formatting
While using search is the new cool, there are other ways to accomplish this, which might be just as simple or even better.

In SharePoint 2010 you would have accomplished a custom view rendering using the XsltListView web part, and apply some fancy xslt to it. And this is the approach I’m going to show, except 2013 have added a new kid to the block to replace xslt, namely JS Link. JS Link is just a cryptic name for Display Templates for list views, and is really powerful in the way that you can use it to customize how a view or columns are displayed.

Let’s get to it!

First, edit the page where you want to show the announcements, click the Insert ribbon and click App Part. Then select the Announcement list (or any list other list).

image
This will display announcements from the default view.
image

The next step involves picking the columns you want, set the filtering and set the sorting. Edit the web part and click Edit the current view to change the view settings.
image

The columns you need are Title, Modified, Body, Description and URL.
image

Sort the view by Modified date in descending order.
image

Filter on announcements which has not yet expired.
image

Create the JS Link file

To get the same rendering as I had in my other posts I created the JS Link template below. For the template I more or less copied the markup from the search template Item_Picture4Lines.html and performed some cleanup on it.

I never seem to go the simplest way, so I had to jump thorough some hoops which is explained below.
The window.mAdcOW.OverrideValueInfo function is there in order to re-use the search helper function which create the <img> tag. Without this override, Srch.ValueInfo throws an error. The function is run as OnPreRender for the JS Link template to ensure it’s registered before the item rendering happens, and is a helper object when getting the <img> tag. An easier approach would to build the <img> myself, and handle missing images etc myself.

Other things to note is that the properties used in the template on ctx.CurrentItem are the columns internal names, for example ctx.CurrentItem.KpiDescription. And to get the friendly date display format I use GetRelativeDateTimeString(ctx.CurrentItem["Modified.FriendlyDisplay"]) which by bad design from Microsoft is a global function on window instead of registered in a namespace. All date properties coming from the view will have an accompying property named <internalname>.FriendlyDisplay.

// Register namespace
window.mAdcOW = window.mAdcOW || {};

// Override function to allow the use of Srch.ContentBySearch.getPictureMarkup
// as it's pretty nice :)
window.mAdcOW.overrideSet = false;
window.mAdcOW.OverrideValueInfo = function()
{
    if (window.mAdcOW.overrideSet) return;
    window.mAdcOW.overrideSet = true;
    window.mAdcOW.origValueInfoConstructor = Srch.ValueInfo.prototype.constructor;
    Srch.ValueInfo.prototype.constructor = function (originalInputValue, managedProperty)
    {
        try {
            window.mAdcOW.origValueInfoConstructor(originalInputValue, managedProperty);
        }
        catch (e) { }        
    }
}

// Function to render each item row
window.mAdcOW.ItemRenderer = {
    itemHtml: function (ctx) {
        var imageValue = new Srch.ValueInfo(ctx.CurrentItem.URL, "ImageUrl");
        var titleValue = new Srch.ValueInfo(ctx.CurrentItem.Title, "Title");

        var pictureMarkup = Srch.ContentBySearch.getPictureMarkup(imageValue, 100, 100, ctx.CurrentItem, "cbs-picture3LinesImg", titleValue, '');

        var linkURL = '';
        var html = '<li><div class="cbs-picture3LinesContainer" data-displaytemplate="ItemPicture4Lines">\
                        <div class="cbs-picture3LinesImageContainer">\
                            <a class="cbs-pictureImgLink" href="' + linkURL + '" title="' + $htmlEncode(ctx.CurrentItem.Title) + '">' + pictureMarkup + '</a>\
                        </div>\
                        <div class="cbs-picture3LinesDataContainer">\
                            <a class="cbs-picture3LinesLine1Link" href="' + linkURL + '" title="' + $htmlEncode(ctx.CurrentItem.Title) + '">\
                                <h2 class="cbs-picture3LinesLine1 ms-accentText2 ms-noWrap"> ' + ctx.CurrentItem.Title + '</h2>\
                            </a>\
                            <div class="cbs-picture3LinesLine2 ms-noWrap"> ' + ctx.CurrentItem.KpiDescription + '</div>\
                            <div class="cbs-pictureLine3 ms-textSmall ms-noWrap">' + STSHtmlDecode(ctx.CurrentItem.Body) + '</div>\
                            <div class="cbs-pictureLine3 ms-textSmall ms-noWrap">' + GetRelativeDateTimeString(ctx.CurrentItem["Modified.FriendlyDisplay"]) + '</div>\
                        </div>\
                    </div></li>';
        return html;
    }
};

// anonymous self-executing function to hook up JS Link templates for the announcement list
(function () {
    var overrideCtx = {};
    overrideCtx.Templates = {};
    overrideCtx.OnPreRender = window.mAdcOW.OverrideValueInfo;
    // header template which includes a CSS reference to the search css which is used
    overrideCtx.Templates.Header = "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/15/1033/styles/Themable/searchv15.css\" /><ul class=\"cbs-List\">";
    overrideCtx.Templates.Item = window.mAdcOW.ItemRenderer.itemHtml;
    overrideCtx.Templates.Footer = "</ul>";
    overrideCtx.BaseViewID = 1;
    overrideCtx.ListTemplateType = 104; //Announcement

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

Hooking up the JS Link file

Once you have created your JS Link file, upload it somewhere below _catalogs/masterpage. You could for example create a folder named JS Link below the Display Templates folder to keep some consistency and manageability. For demo purposes I upload my file – announcement_JS Link.js directly below Display Templates.

image

When you upload the file you are presented with metadata properties to set for the file. Pick Javascript Display Template, set the target control to View, make it an Override, the template can be used on any site and for Announcement lists (104).

image

The last step is to go back to the page where you added the Announcement list. Edit the web part, expand the Miscellaneous section and paste the following in the JS Link field.

~sitecollection/_layouts/15/SP.Init.js|~sitecollection/_layouts/15/Search.ClientControls.js|~sitecollection/_layouts/15/Search.CBS.js|~sitecollection/_catalogs/masterpage/Display Templates/announcement_JS Link.js

image

The string is a pipe (|) separated list of the scripts needed, and they will be loaded in order. As I’m re-using functions from search I have included references to two search .js files and SP.Init which is a dependency first, and at the end I list a reference to announcement_JS Link.js, which is the actual display template.

Pipe separating script files in this property is a great way to load for example jQuery if it’s not included in the master page. Saving you changes you should see something like below, equal to the Content Search rendering.

image

I would like to thank Chris O’Brian (@ChrisO_Brien) and Paul Hunt (@cimares) for their posts on JS Link which helped me along the way.