Wednesday, February 12, 2014

Creating a custom display template for a content search web part

In my previous post (Sample on how to use Content Search to display an announcement list) I went over how to set up content search against an announcement list which had some additional fields added to it via a custom content type.
I will build on this sample and create a custom display template, which will add one more line to allow for the Body field of the announcement as well.
Topics covered in this post:
  • Create a custom display template
  • Render rich text fields properly
  • Render dates in a better fashion

My final template is at the end of this post.
To get started make sure you have activated Publishing Infrastructure on the site collection. This will provision the html versions of the display templates as well as hook up the event receiver which convert them to .js files.

Next up, make a copy of Item_Pictures3Lines.html which is located in /_catalogs/masterpage/Display Templates/Content Web Parts. I named my copy Item_Picture4Lines.html as I’m planning to add one more line to the template displaying the body of the announcement items.

First change the <title> tag of the .html file to something unique; for example

<title>Picture on left, 4 lines on right</title>

This ensures you have a unique name for your template and this is the name displayed in the drop-down of the Content Search web part when picking a template.

Further you have to add a new property section for Line 4, as well as appropriate javascript and html to cater for the new line. Basically copy/paste everything which has Line 3 in it, and rename to 4.

Once saved you should be able to pick it for the content search web part, and add BodyOWSMTXT for the body managed property.

image

After you’re changes are saved you should see a result similar to the one below. The part to note about this is that the body text appear html encoded.

image

Fixing this is not hard once you get around to knowing some of the helper JavaScript methods available to you. Simply replace

<div class="cbs-pictureLine3 ms-textSmall ms-noWrap" id="_#= line3Id =#_">_#= line3 =#_</div>

with

<div class="cbs-pictureLine3 ms-textSmall ms-noWrap" id="_#= line3Id =#_">_#= STSHtmlDecode(line3.value) =#_</div>

and it renders correct.

image

The next item to fix is the date rendering. Again, I’m using a method available from SharePoint.

In the <script> tag in the template add the following line to reference dateTimeUtil.js

$includeScript(this.url, "~sitecollection/_layouts/15/SP.dateTimeUtil.js");

and replace

<div class="cbs-pictureLine3 ms-textSmall ms-noWrap" id="_#= line4Id =#_">_#= line4 =#_</div>

with

<div class="cbs-pictureLine3 ms-textSmall ms-noWrap" id="_#= line4Id =#_">_#= SP.DateTimeUtil.SPRelativeDateTime.getRelativeDateTimeString ( new Date(line4), true , SP.DateTimeUtil.SPCalendarType.none, false ) =#_</div>

image

Complete Template Code

<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
<head>
    <title>Picture on left, 4 lines on right</title>
    <!--[if gte mso 9]><xml>
    <mso:CustomDocumentProperties>
    <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
    <mso:ManagedPropertyMapping msdt:dt="string">'Picture URL':'PublishingImage;PictureURL;PictureThumbnailURL','Link URL':'Path','Line 1':'Title','Line 2':'Description','Line 3':'','Line 4':'','SecondaryFileExtension','ContentTypeId'</mso:ManagedPropertyMapping>
    <mso:MasterPageDescription msdt:dt="string">$Resources:Microsoft.Office.Server.Search,DisplayTemplateDescription_CBSDefaultItem</mso:MasterPageDescription>
    <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
    <mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType>
    <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
    </mso:CustomDocumentProperties>
    </xml><![endif]-->
</head>
<body>
    <script>
        $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
        $includeScript(this.url, "~sitecollection/_layouts/15/SP.dateTimeUtil.js");
    </script>

    <div id="Item_Picture4Lines">
        <!--#_

        var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_picture3Lines_");

        var linkURL = $getItemValue(ctx, "Link URL");
        linkURL.overrideValueRenderer($urlHtmlEncode);

        var line1 = $getItemValue(ctx, "Line 1");
        var line2 = $getItemValue(ctx, "Line 2");
        var line3 = $getItemValue(ctx, "Line 3");
        var line4 = $getItemValue(ctx, "Line 4");

        var pictureURL = $getItemValue(ctx, "Picture URL");
        var pictureId = encodedId + "picture";
        var pictureMarkup = Srch.ContentBySearch.getPictureMarkup(pictureURL, 100, 100, ctx.CurrentItem, "cbs-picture3LinesImg", line1, pictureId);

        line1.overrideValueRenderer($contentLineText);
        line2.overrideValueRenderer($contentLineText);
        line3.overrideValueRenderer($contentLineText);
        line4.overrideValueRenderer($contentLineText);

        var containerId = encodedId + "container";
        var pictureLinkId = encodedId + "pictureLink";
        var pictureContainerId = encodedId + "pictureContainer";
        var dataContainerId = encodedId + "dataContainer";
        var line1LinkId = encodedId + "line1Link";
        var line1Id = encodedId + "line1";
        var line2Id = encodedId + "line2";
        var line3Id = encodedId + "line3";
        var line4Id = encodedId + "line4";

        var dataDisplayTemplateTitle = "ItemPicture4Lines";
         _#-->
        <div class="cbs-picture3LinesContainer" id="_#= containerId =#_" data-displaytemplate="_#= $htmlEncode(dataDisplayTemplateTitle) =#_">
            <div class="cbs-picture3LinesImageContainer" id="_#= pictureContainerId =#_">
                <!--#_
                if(!linkURL.isEmpty)
                {
                _#-->

                <a class="cbs-pictureImgLink" href="_#= linkURL =#_" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= pictureLinkId =#_">
                    <!--#_
                    }
                    _#-->
                    _#= pictureMarkup =#_
                    <!--#_
                    if(!linkURL.isEmpty)
                    {
                    _#-->
                </a>
                <!--#_
                }
                _#-->
            </div>
            <div class="cbs-picture3LinesDataContainer" id="_#= dataContainerId =#_">
                <a class="cbs-picture3LinesLine1Link" href="_#= linkURL =#_" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= line1LinkId =#_">
                    <h2 class="cbs-picture3LinesLine1 ms-accentText2 ms-noWrap" id="_#= line1Id =#_"> _#= line1 =#_</h2>
                </a>
                <!--#_
                if(!line2.isEmpty)
                {
                _#-->
                <div class="cbs-picture3LinesLine2 ms-noWrap" title="_#= $htmlEncode(line2.defaultValueRenderer(line2)) =#_" id="_#= line2Id =#_"> _#= line2 =#_</div>
                <!--#_
                }
                if(!line3.isEmpty)
                {
                _#-->
                <div class="cbs-pictureLine3 ms-textSmall ms-noWrap" id="_#= line3Id =#_">_#= STSHtmlDecode(line3.value) =#_</div>
                <!--#_
                }
                if(!line4.isEmpty)
                {
                _#-->                
                <div class="cbs-pictureLine3 ms-textSmall ms-noWrap" id="_#= line4Id =#_">_#= SP.DateTimeUtil.SPRelativeDateTime.getRelativeDateTimeString ( new Date(line4), true , SP.DateTimeUtil.SPCalendarType.none, false ) =#_</div>
                <!--#_
                }
                _#-->
            </div>
        </div>
    </div>
</body>
</html>