June 2014

Volume 29 Number 6

SharePoint : Using JSLink with SharePoint 2013

Pritam Baldota | June 2014

Working with the SharePoint UI has always been something of a challenge for developers. However, a new feature in SharePoint 2013 called JSLink offloads the burden of using XSLT and provides a much easier and more responsive way to display custom fields on the client. JSLink is a property that controls rendering of fields, items and even Web Parts through a JavaScript file.

This article will explore the use of JSLink with the help of two demo scenarios. The first scenario will demonstrate how you can use color-coded messages to indicate task completion, for example, as shown in Figure 1.

Using a Custom Column for a Task List to Show Status
Figure 1 Using a Custom Column for a Task List to Show Status

The second scenario is more complex. It will demonstrate how to implement an image gallery that provides a callout for each image to show metadata and allow downloading at different resolutions, as shown in Figure 2.

Multiple Resolutions of an Image Available for Download
Figure 2 Multiple Resolutions of an Image Available for Download

I’ll also delve into customizing the New and Edit form fields. Before I get started, though, I’ll take a look at the basics of client-side rendering in SharePoint 2013 and how JSLink adds value to it.

Client-Side Rendering

Client-side rendering refers to the displaying of data on a page using technologies that operate on the client, such as JavaScript, HTML and CSS. Because the technology runs in the client’s browser, it’s more responsive and efficient, thereby reducing the load on the Web server. Earlier versions of SharePoint (2010, 2007) used XSLT to style elements, which is generally more complex to work with and slower in performance compared with JavaScript. SharePoint 2013 still supports XSLT, but has two additional techniques for customizing results on the client side—display templates, which define the way a SharePoint 2013 Search Web Part renders results (see bit.ly/1i5fM6k for more information) and, the subject of this article’s focus, JSLink.

JSLink is a property that can be used with fields, Web Parts, list forms and content types. With this property, you can add JavaScript files, opening up broad possibilities for customization. Each JavaScript file you add is prefaced with a SharePoint token and separated by the pipe symbol (|), like this: ~site/style library/­mycustom.js|~site/style library/mycustom2.js. If the JavaScript files don’t contain any relevant rendering code, default rendering of the element is applied.

SharePoint provides several tokens (static and dynamic) that are useful for constructing a context-specific URL. Here are some important SharePoint tokens for dynamic URL construction:

  • ~site—refers to the URL of the current Web site.
  • ~sitecollection—refers to the URL of the parent site collection of the current Web site.
  • ~layouts—refers to _layouts/15 with respect to the Web application.
  • ~sitecollectionlayouts—refers to the layouts folder in thecurrent site collection (such as /sites/mysite/_layouts/15).
  • ~sitelayouts—refers to the layouts folder in the current site (such as site/mysite/mysubsite/_layouts/15).

SharePoint has more tokens for URL construction. To learn more about URL strings and tokens, see the Dev Center at bit.ly/1lpYuAP.

Client-side rendering using JSLink has a number of advantages over XSL/XSLT. In the first place, it uses JavaScript, with which most Web developers are already comfortable. XSLT is somewhat more complex to develop and debug, so JSLink can reduce development time with no loss of accuracy.

Rendering a view on the client using JavaScript, HTML, and CSS avoids unnecessary loads on the server, improving overall performance and reducing page response times. Client-side processing makes the UI highly responsive.

Furthermore, you can customize all or part of a view using JSLink. For example, if you want to customize just a specific field, you can tailor the rendering logic for that field only; the rest of the view will be rendered using the default logic. With JSLink, you can use any valid JavaScript, including external plug-ins such as jQuery, in combination with HTML and CSS.

Of course, every technology has some disadvantages, and so does JSLink. For example, if a user has JavaScript blocked in his browser, JSLink won’t work. Server-side rendering with XSLT will still show the same experience, but performance might suffer.

Performance might also suffer if a user’s browser or system is old or underpowered because it may take more time to execute the script.

Finally, crawlers are unlikely to understand the dynamic content generated by AJAX/JavaScript; they require static data rendered with HTML. So, if you have a public-facing site, JSLink may not be your best choice.

The JSLink JavaScript file reference can be set in multiple ways using the server object model, Windows PowerShell, Element.xml via Features, the Web Part Properties window and the client object model. Following is some sample code for each approach.

The Server Object Model: To set the JSLink property of, say, List forms, you access the SPForm object using the list’s Forms collection and then set the JSLink property to the SPForm object:

SPWeb web = SPContext.Current.Web;
SPList list = web.Lists.TryGetList("MyTasks");
if (null != list)
{
  SPForm newForm = list.Forms[PAGETYPE.PAGE_NEWFORM];
  if (null != newForm)
  {
    newForm.JSLink = "~/mycustom.js";
  }
}

Windows PowerShell: To set the JSLink property of, for example, a custom field of a list, you  access the field object using the list’s Fields collection and then set the JSLink property to the field object:

$web = Get-SPWeb
$field = $web.Fields["MyCustomField"]
$field.JSLink = "~/layouts/mycustom.js"
$field.Update()
$web.Dispose()

The Element.xml file: To set the JSLink property of a custom field of a list, you add a Field node into the Element.xml file:

<Field ID="{eb3eed37-961b-41bd-b11c-865c16e47071}"
Name="MyCustomField" DisplayName="Custom Columns"
Type="Text" Required="FALSE" Group="JSLink Demo"
JSLink="~site/style library/JSLinkDemo/jquery-1.10.2.min.js|
~site/style library/JSLinkDemo/customview.js">
</Field>

Notice that by using the pipe symbol you can add multiple JavaScript files.

The Web Parts properties dialog: To set the JSLink property of a Web Part, you modify its properties. Go to Web Part | Edit Properties | Miscellaneous and set the JSLink property.

The Client-Side Object Model (CSOM): You can set the JSLink of a field using the CSOM. Note that you can’t update the site column property via JavaScript directly, you need to update it using list-associated fields. If you try to update at site column level, you’ll get this error:

This functionality is unavailable for fields not associated with a list ...

 

This code shows how to correctly update the JSLink property for a field in a list via the JavaScript CSOM:

fieldCollection = taskList.get_fields();
this.oneField = fieldCollection.getByInternalNameOrTitle("MyCustomField");
this.oneField.set_description("MyNewFieldDescription");
this.oneField.update();

For more information on this example, see the MSDN documentation at bit.ly/1i9rlZR.

In order to work with JSLink, in the custom JavaScript file you need to override this method:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides()

This method requires a template object to be passed to it. To define the template object, you must specify a property and a render method for each view (such as View, Add and Edit).

The template object for registering the method has properties such as Header, Body, Footer, OnPreRender, OnPostRender, Group, Item and Fields, which can be leveraged to override the default rendering logic of a view. For example, to modify the custom fields View, Display, Edit and New, you provide the following information for the template object’s Fields property:

siteCtx.Templates.Fields = {
  // MyCustomField is the Name of our field
  'MyCustomField': {
  'View': customView,
  'DisplayForm': customDisplayForm,
  'EditForm': customNew,
  'NewForm': customEdit
  }
};

The methods referenced in this code, such as customView and customDisplayForm, contain the actual rendering logic for this field.

Finally, you call the RegisterTemplateOverrides method of TemplateManager to apply the custom view, like so:

 

// Register the template to render custom field
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(siteCtx);

This method will take care of the rest of the rendering of the view based on your custom logic.

Keep in mind that when you use multiple instances of a list view Web Part on the same page and apply JSLink to one of the instances, the layout of all other list view Web Parts changes also, because internally SharePoint uses common rendering logic. To avoid this problem, you need to make sure the BaseViewID value won’t conflict with existing ViewIDs, so change the BaseViewID property of the context before overriding, for example, ctx.BaseViewID = 1000.

Now I’ll walk you through the two demonstration scenarios.

Scenario 1: Display Color-Coded Task Completion Status

This scenario shows task completion status using color coding for a Task List—red for an incomplete task and green for a completed task.

In the example, I’ll customize the View template for the custom field, as shown here:

// View Page custom rendering
function customView(ctx) {
if (ctx != null && ctx.CurrentItem != null) {
  var percentCompete = parseInt(ctx.CurrentItem.PercentComplete);
  if (percentCompete != 100) {
    return "<span style='color:red'>Incomplete Task</span>";
  }
  else {
    return "<span style='color:green'>Task Completed</span>";
  }
}
}

The customView method receives the current render context as an input parameter from SharePoint internally. This input context has lots of other properties related to List, View, Current Item and so forth.

The context’s currentItem property gives access to the current row item of a list. Through this property you can access all fields available in the List. Note that if the Field isn’t available in a selected view, you’ll get an error when accessing the field value.

To begin, open Visual Studio 2013 and choose a SharePoint 2013 Empty Project.

Step 1: From the Add New Item menu, add a Site Column to the empty project. To create a Site Column, add the Field information in an Element.xml file, as shown in Figure 3. As you can see, the JSLink property references two JavaScript files: the JQuery library and a custom view JavaScript file from the Style Library. Note that the hierarchy of files should be maintained from left to right (dependent files should be referenced first).

Figure 3 Referencing the JavaScript Files

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="https://schemas.microsoft.com/sharepoint/"> 
  <Field
    ID="{eb3eed37-961b-41bd-b11c-865c16e47071}"
    Name="MyCustomField"
    DisplayName="Custom Columns"
    Type="Text"
    Required="FALSE"
    Group="JSLink Demo"
    JSLink="~site/style library/JSLinkDemo/jquery-1.10.2.min.js|
            ~site/style library/JSLinkDemo/customview.js">
  </Field>
 
</Elements>

Step 2: Add another New Item, a Scripts module to store custom JavaScript and other resources files.

Step 3: Add the JQuery Library to the Scripts module. Create a new JavaScript file called CustomView.js.

Step 4: Rename the default Feature from Feature 1 to JSLinkDemo, or create a new Feature that includes the Site Columns and Scripts module. Set the scope to Site as it’s the Site Column that will deploy.

Step 5: Deploy the solution. After you deploy the solution, you’ll see the Column added to Site Columns by going to Site Settings | Site Columns.

Step 6: Create a task list called MyTasks by adding an app from the Site contents page.

Step 7: Add a custom column to the MyTasks list from the List Settings Menu on the ribbon.

Step 8: Add a custom column as created using Steps 1 to 5 by clicking Add from existing site columns on the List Settings page. Filter the group using JSLink Demo, choose Custom Column and add it to the list.

This completes the implementation of the first scenario, which displays a task’s completion status. As you can see in Figure 1, the custom status shows as either Task Completed in green or Incomplete Task in red.

In this scenario I’ll customize the built-in image gallery rendering to display in a custom tabular format, with callouts to download multiple resolution images.

To do this, I customize the Item, Header and Footer properties of Template Field. The following code sets up the initial step:

(function () {
  var overrideContext = {};
  overrideContext.ListTemplateType = 109;
  overrideContext.Templates = {};
  overrideContext.Templates.Item = customItem;
  SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext);
})();
 
function customItem() {
  return "ItemRow";
}

I’ve set ListTemplateType to 109, which SharePoint predefines as PictureLibrary. (You’ll find all the predefined list template types in SharePoint 2013 at bit.ly/1qE8UiY.) This code will render rows with text returned from the customItem method.

Now, I’ll replace the static text with the actual properties of Image (Title, URL and other fields) to start rendering the image. To replace the default new picture link and header columns, I’ll customize the header and footer properties to set some HTML text:

overrideContext.Templates.Header = "<b>Custom Image Gallery View</b>";
overrideContext.Templates.Footer = "<b>Custom Footer</b>";
function customItem() {
  return "<br /><img src='" + ctx.CurrentItem.FileRef +
  "' width='190' height='190' />;
}

I want to display the images in tabular format. To do so, I have to add some CSS and HTML for the layout in the customItem method. First, I add an empty div container to the header template, which will be used later on to hold the actual data from Item and Footer rendering. Within the Item rendering, I create dynamic HTML and store it in a global variable. In the Footer rendering template, I assign the dynamic data to the div container. Figure 4 shows the complete code to render a tabular format image gallery.

Figure 4 Displaying Images in Tabular Format

function customItem(ctx) {
  // Grid contains 4
  if (ctx.CurrentItemIdx % 4 == 0) {
    // Start new row
    tableData+= "<div style='clear:both'></div>"
  }
 
  tableData += "<div style='margin:5px;border:1px solid #666;float:left;"+
  "width:100px;height:100px' onmouseover=\""+
  "ShowCallout(this,'" + ctx.CurrentItem.Title + "','" +
  ctx.CurrentItem.FileDirRef + "','" + ctx.CurrentItem.FileLeafRef
  + "');\"><img src='" + ctx.CurrentItem.FileRef +
  "' width='100' height='100' /></div>";
 
return "";
}
 
// Custom Footer Method
function customFooter(ctx) {
  // Append Dynamic-generated data to container div
  $("#customImageGalleryContainer").html(tableData);
  return "";
}

Now I want to display a callout on mouse over for each image, and show additional metadata such as Title, Brief Info and so on, along with different image resolutions for download. SharePoint 2013 provides a callout framework to show contextual information via a callout.js JavaScript file, using the CalloutManager global object. I’ll leverage that framework to show default callouts. First, I create a custom method called ShowCallout to determine if there are any existing open callouts; if there are, I will close them using the default closeAll method from the CalloutManager:

function ShowCallout(sender, itemId, title, brief, directory, filename) {
  CalloutManager.closeAll();
}

Before moving ahead with the implementation, you need to understand how to get images of different resolutions. In SharePoint 2013, by default an image is created in two different sizes when it’s uploaded to an image library. For example, if you upload sample.png to library path /imageLibraryName/, SharePoint automatically creates both a thumbnail and a Web-size image: /imageLibrary­Name/_t/Sample_png.jpg and /imageLibraryName/_w/­Sample_png.jpg. In these URLs, _t represents Thumbnail and _w represents Web, and the file extension gets appended to the filename with a prefix underscore (_) separator. 

The ShowCallout function uses these URLs for the original image, as well as for images of different resolutions, and stores them in different variables as shown here (Filename and directory are ShowCallout parameters):

var fname = filename.replace(".", "_");
var thumbnail = directory + "/_t/" + fname + ".jpg";
var medium = directory + "/_w/" + fname + ".jpg";
var full = directory + "/" + filename;

For this scenario I use the CalloutManager createNewIfNecessary method, which creates the callout only if there’s no callout at the target launch point. If there is a callout, the method returns the existing object reference. (To read more about CalloutManager, visit bit.ly/1kXH7uU.) Figure 5 shows the complete code, and Figure 6 shows the resulting output.

Figure 5 The Updated ShowCallout Function

function ShowCallout(sender, itemId, title, brief, directory, filename) {
  // Close fists all callouts if opened
  CalloutManager.closeAll();
  var fname = filename.replace(".", "_");
  var thumbnail = directory + "/_t/" + fname + ".jpg";
  var medium = directory + "/_w/" + fname + ".jpg";
  var full = directory + "/" + filename;
  var calloutContent = "<img src='" + medium + "' width='50%'
    height='50%' /><br/>" +
  brief + "&nbsp;<a href='" + directory + "/Forms/DispForm.aspx?ID=" +
  itemId + "'>View Image</a><br />" +
  "<a href='" + thumbnail + "' target='_blank'>
    Thumbnail</a>&nbsp;&nbsp;|&nbsp;&nbsp;" +
  "<a href='" + medium + "' target='_blank'>
    Web Size</a>&nbsp;&nbsp;|&nbsp;&nbsp;" +
  "<a href='" + full + "' target='_blank'>Original Size</a>";
 
  var calloutRef = CalloutManager.createNewIfNecessary({
    ID: 'call_' + itemId, launchPoint: sender,
    beakOrientation: 'leftRight', title: title,
    content: calloutContent,
    contentWidth: 610
  });
 
  calloutRef.open();
}

Custom Image Gallery with Callout Showing Metadata
Figure 6 Custom Image Gallery with Callout Showing Metadata

To apply the custom rendering, create a site page and add the image library List View Web Part. Go to the Edit WebPart properties dialog and set JSLink property to ~/site/Style Library/JSLinkDemo/jquery-1.10.2.min.js|~/site/Style Library/JSLinkDemo/Custom­ImageGallery.js. Click Apply and Save Page. After refreshing the page, you’ll see a Gallery like the one shown in Figure 6.

You can customize the New and Edit form fields using a similar approach, though with some variations. Here you need to consider such things as input validation, storing data from input field to list item, displaying stored data from a list item in an input field and so forth.

Next, I’ll take a look at the implementation details of a complex, multi-column custom field scenario as shown in Figure 7.

Multi-Column Custom Fields of a List Using JSLink
Figure 7 Multi-Column Custom Fields of a List Using JSLink

Step 1: Using the Element.xml file, create a Note (a custom field for text under Site Columns with the NumLines property set to, say, 1,000 (or whatever suits your needs). This custom field overrides the rendering on the New and Edit form.

siteCtx.Templates.Fields = {
  "MyComplexField": {
  'EditForm': customComplexNewOrEdit,
  'NewForm': customComplexNewOrEdit
}
};

I use the same customComplexNewOrEdit method for both view New and Edit.

Step 2: Set up the form context, which is required for setting up validators to read a value from the custom view and to save it back to a list. To set up the form context you use the SPClientTemplates.Utility.GetFormContextForCurrentField method. This method accepts Render Context as parameter, which is provided by SharePoint internally. Here’s some sample code showing the new or edit custom render method:

 

function customComplexNewOrEdit(ctx) {
  if (ctx == null || ctx.CurrentFieldValue == null)
  return '';
 
  var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx);
  if (formCtx == null || formCtx.fieldSchema == null)
    return '';
}

Step 3: After receiving the form context, you need to register callback handlers with the current form to enable validation, get a field’s value and save the value into the appropriate field on the custom view generated from JSLink. Every time a user clicks the save button on the List form, SharePoint internally invokes a callback handler attached to the fields using formCtx.registerGetValueCallback(filedName, callback). This callback handler reads the value from the field before saving it to list item:

formCtx.registerGetValueCallback(formCtx.fieldName, function () {
  // Read value from this callback and assign to the field before save
  return "";
});

This is an empty callback handler. You have to customize the code in order to read the values from the selected controls on the form and return the string representation of the values.

Step 4: To add validators, you need to first create a validator container in which to register validators for the various fields. I do this using the SPClientForms.ClientValidation.ValidatorSet object. I can use the RegisterValidator method of Validator Container to register multiple validators.

You need to register an error callback hander, as well, to show errors. To do so, I’ll use the registerValidationErrorCallback method of form context. This method requires two parameters, the ID of the HTML container element (div in this example) and errorResult, which is returned from SharePoint internally based on validation failure. Error messages will append to the container element provided to this method. In my example I need to add a required field validator, and the complete code for validation is shown in Figure 8.

Figure 8 Validation Code

var validatorContainer = new SPClientForms.ClientValidation.ValidatorSet();
if (formCtx.fieldSchema.Required) {
  validatorContainer.RegisterValidator(
  new SPClientForms.ClientValidation.RequiredValidator());
}
 
if (validatorContainer._registeredValidators.length > 0) {
  formCtx.registerClientValidator(
  formCtx.fieldName, validatorContainer);
}
 
formCtx.registerValidationErrorCallback(
  formCtx.fieldName, function (errorResult) {
    SPFormControl_AppendValidationErrorMessage(
    errorContainer, errorResult);
});

Step 5: You need to add custom HTML controls to represent the field on the new or edit page. I’ll have one text box, one dropdown list with dynamic values from another list and one dropdown list with static values. I’ll create a table, add these controls to the table and return the HTML as output from the custom Add/Edit method.

To load the data dynamically, I use the REST API to read the data from another list and use it to fill the title in this list. To find out more about the REST API in SharePoint, see bit.ly/1cVNaqA.

Figure 9 shows the code for rendering input controls dynamically.

Figure 9 Rendering Input Controls Dynamically

// Render input fields
var inputFields = "<table>";
...
inputFields += "</table>";
 
// Get List data from REST API
$.ajax({
...
success: function (data) {
  // Add options to the dynamic dropdown list
},
error: function (data) {
  // Error handler
}
});
 
 
return inputFields;

Step 6: To save a multi-field value to a list, I put all of the fields’ values into a single string using the string separator, and create a custom format like (fieldname:value)(fieldname:value), a kind of key-value pair. I’ll do the string construction in registerGetValueCallback.

Step 7: To display an existing value in the edit form, I parse the string stored as a key-value pair,  saving and assigning the values to respective input controls. This is accomplished using the same custom render method as when constructing custom HTML.

Wrapping Up

You can debug JSLink using browser-based developer tools such as the Internet Explorer developer tools, Firebug and the like. If you put a breakpoint in JavaScript after it renders on the client, you can debug the JavaScript just like C# .NET code. When you access the context in the View method, you can get a reference to the current Item through which you can see the values for each field of the current item. You can use the Internet Explorer developer tools console window to access any property of the current object.  

JSLink provides a simple way to customize any SharePoint view using a pure client-side script, without writing a single line of server-side code. You can use any JavaScript-based plug-in with JSLink. Download the sample demo project at msdn.microsoft.com/magazine/msdnmag0614.


Pritam Baldota is a SharePoint consultant at Microsoft Services Global Delivery with more than nine years of industry experience. In his leisure time he blogs at pritambaldota.com. Reach him at pritam@pritambaldota.com.

Thanks to the following Microsoft technical experts for reviewing this article:Sanjay Arora, Subhajit Chatterjee and Paresh Moradiya
Sanjay Arora – Sanjay is a professional development resource manager at Microsoft Services Global Delivery.

Subhajit Chatterjee is a Senior Consultant with Microsoft Services Global delivery with more than 12 years of industry experience. He has executed many turnkey and complex engagements at GD.

Paresh Moradiya has 11+ years of Experience in Software Industry. He is currently working as Consultant with Microsoft since last 5 years. He is a SME in SharePoint Web Content Management and SharePoint Social.