Navigation

Wednesday 21 August 2013

Customising the Content Search Web Part - Part 2 - Custom Display Templates with JavaScript

This is the second post in a series I will be writing on the Content by Search Web Part (aka CSWP).
  1. What you get in the box
  2. Custom Display Templates with JavaScript (this post)
  3. Going Old Skool with XSLT
  4. Packaging & Deployment in Visual Studio
So if you've read Part 1 (what you get in the box) then you should have a pretty good idea of what the CSWP can do by now and what Display Templates and Property Mappings are (if you don't then go back and read Part 1 .. I won't be explaining them again).

So now we move onto the brave new world of building and defining your own custom Display Templates. The first thing you need to know is that Display Templates are stored in the Master Page Gallery (No, I don't know why) specifically in a sub-folder called "Display Templates".

Item Templates in the Master Page & Page Layouts Gallery
In here you will find two types of file:
  • HTML files - these are the actual Display Templates (and what you will copy/create when you create your own custom template)
  • JS files - These are the "compiled" JavaScript files which are automagically created by SharePoint when you add / modify one of the HTML templates

Display Template basics - Using JavaScript
The out of the box display template files actually include some pretty useful comments which tell you how this works, but basically you have two "tokens" that you need to use:

To Write JavaScript explicitly you need to use HTML comments with #_ and _# at each end. This tells SharePoint that it needs to take whatever is inside these tokens and "compile" it into JavaScript in the finished file. For example:

<!--#_ var myString = 'Hello World'; _#-->

This allows you to write pretty much any JavaScript that you like. You can use this JavaScript to retrieve values from your Property Mappings as follows:

<!--#_ var linkURL = $getItemValue(ctx, "Link URL"); _#-->

In the above example "Link URL" is the name of the Property Mapping. If you then want to use those variables in your HTML you simply wrap then in =#_ and _#= tokens. For example:

<a href="_#= linkURL =#_">
  <span> _#= myString =#_</span>
</a>

It really is that simple!

But before we look at actually creating a new Display Template, what we really need is a problem to solve (I find it helps to focus the mind) ..

A suitable problem - Pictures, Publishing Pages and Image Renditions, oh my!
One of the great new features in SharePoint 2013 is "Image Renditions" (explained in great detail by Waldek Mastykarz). The cool thing is that if you have registered an Image Rendition with a desired width / height then you can include those as URL attributes and SharePoint will resize the image server side! This is a fantastic feature, it (drastically) reduces image sizes and ensures that the output is consistently rendered.

The out of the box display templates will automatically use these Image Renditions (the "Large Picture" template for example will ask SharePoint for an image which is 468x220, which matches one of the out of the box image renditions).

However when you try to use Image Renditions with the CSWP then you get some pretty odd results. To demonstrate this I have uploaded 4 photos from one of my holidays a few years back. I then created a Publishing Page for each photo and selected different Image Renditions for each page.

I then displayed then using the CSWP using the "Large Picture" item template and here are the results:


CSWP displaying the actual photos in the Asset Library


CSWP displaying the Publishing Pages (and pulling the associated image)
 As you can see the second one looks a bit screwy as all of the images are different sizes. So what on earth is happening? Well, on checking the URL for one of the images which is being returned I can see that the image URL is correctly picking up the including the Rendition ID that I picked on my publishing page.

..photos/DSC_1002.JPG?RENDITIONID=1&width=468&height=220

This is bad, because when SharePoint renders an image it will first look for the RenditionID and if it finds one it will ignore the width and height attributes!

The solution - Custom Display Template
So in order to fix this we need to create ourselves a custom Display Template so we can strip out that RenditionID attribute using JavaScript so the images served by SharePoint come out in the correct size.

So first off, I went and downloaded a copy of the Item_LargePicture.html file (which I used as my starting point) and renamed it (to "Item_LargePictureFixed.html").

The next thing to do is to rename the <title> attribute in the file. This is what gets used in SharePoint to identify the template in the CSWP web part properties.

<title>Large Picture (FIXED)</title>

Now, I also wanted to add some more detail to my display template as just showing the title wasn't that great (bringing back the publishing date and author for the page would be cool) .. and it also allows me to demonstrate how the Property Mappings work.

So the next thing is to look for an XML element near the top of the file called mso:ManagedPropertyMapping. This contains all of the Property Mappings that the CSWP will pick up when your template is in use.

When you define Property Mappings you can also define the search properties it should look for and the format you need to use is:
<Mapping Name>:<Search Property1>;<SearchProperty2>
Each mapping is separated by a comma (,) so my custom mappings were
  • Image Title (mapped to "Title")
  • Image Date (mapped to "ImageDateCreated" and "LastModifiedTime")
  • File Author (mapped to "Author")

'Image Title':'Title','Image Date':'ImageDateCreated;LastModifiedTime','File Author':'Author'

Don't forget that our properties map to Search Properties .. so if the field isn't being indexed by Search then you won't be able to use it!

Once we had our mappings in place we can pull it all into variables using JavaScript. This includes a (slightly nasty) sub-string function to pull out any URL attributes for the Image URL

<!--#_

var pictureURL = $getItemValue(ctx, "Picture URL");

// get the picture URL as a string
var cleanPictureURL = pictureURL.toString();

// check if it contains any query string arguments
if(cleanPictureURL .indexOf("?") != -1)
{
  // if it does, then strip them out
  var renditionIndex = cleanPictureURL .indexOf("?");
  cleanPictureURL = cleanPictureURL .substring(0, renditionIndex);
}

// add the width/height attributes back in for the image renditions
cleanPictureURL = cleanPictureURL + "?width=468&height=220";

 
// get our three other property values
var imageTitle = $getItemValue(ctx, "Image Title");
var imageDate = $getItemValue(ctx, "Image Date");
var fileAuthor = $getItemValue(ctx, "File Author");

_#-->
 


Once that was done all we needed was to make sure our variables were used in the right parts of the HTML. I then uploaded the HTML file (which effectively installs the new Display Template) and wired it up in the Web Part.

New Display Template with new Property Mappings being displayed
 
New "fixed" Display Template with correct sized images, and new property mappings being displayed
If you want to take a closer look then you can download the example HTML file here:
http://sdrv.ms/172o9rM

So that is it for CSWP Display Templates .. in part three we will look at going old school with XSLT rendering.



No comments:

Post a Comment

This blog has been moved to www.martinhatch.com

Note: only a member of this blog may post a comment.