CSR and MDS: 4 Steps to get the Best from both Worlds

I’m going to show you how to create a CSR (or JSLink) script that works with and without MDS and follows coding best practices.

Scenario

Both CSR (Client-Side Rendering) and MDS (Minimal Download Strategy) are new concepts in SharePoint 2013.
CSR lets you control how your list data is displayed using Javascript.

CSR

MDS improves rendering performance where large parts of the page do not change, providing a more fluid navigation experience.

MDS.png

Problem

The problem is that CSR works fine with MDS only on the first page request or after a refresh. As soon as the user switches between views or navigates around and then comes back to the original page, the CSR customization vanishes and the default rendering takes place again.

I’ll try to explain what happens here.
When the page is first rendered, the MDS system receives the custom script for the first time and puts it in a list of already executed scripts. When subsequent pages are requested, MDS checks its own list of already executed scripts and will simply not execute them again.

However you want the CSR script to execute every single time… So, how to achieve that without disabling MDS feature and keeping its performance gains?

Solution

The following CSR script comprises 4 steps:

  1. Following Coding Best Practices
  2. Addressing MDS Garbage Collection issue
  3. Addressing MDS Script Reload issue
  4. Making it work without MDS as well
Type.registerNamespace('Client');
Client.Project = Client.Project || {};
Client.Project.Templates = Client.Project.Templates || {};
Client.Project.Functions = Client.Project.Functions || {};

Client.Project.Functions.Display = function (context) {
var currentValue = context.CurrentItem.MyFieldInternalName
if (currentValue > 0) {
return currentValue;
}
else {
return '<span style="color:red">' + currentValue + '</span>';
}
}

Client.Project.Templates.Fields = {
'MyFieldInternalName': {
'DisplayForm': Client.Project.Functions.Display,
'View': Client.Project.Functions.Display,
'NewForm': null,
'EditForm': null
}
}

Client.Project.Functions.OverrideTemplates = function () {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(Client.Project);
}
Client.Project.Functions.MdsOverrideTemplates = function () {
var scriptUrl = _spPageContextInfo.siteServerRelativeUrl + "/SiteAssets/CSRScript.js";
Client.Project.Functions.OverrideTemplates();
RegisterModuleInit(scriptUrl,Client.Project.Functions.OverrideTemplates);
}

if (typeof _spPageContextInfo != "undefined" && _spPageContextInfo != null) {
Client.Project.Functions.MdsOverrideTemplates();
} else {
Client.Project.Functions.OverrideTemplates();
}

Let’s examine the above script in more details.

1. Following Coding Best Practises

Define your own namespaces to avoid polluting the global one.

Client.Project = Client.Project || {};
Client.Project.Templates = Client.Project.Templates || {};
Client.Project.Functions = Client.Project.Functions || {};

2. Addressing MDS Garbage Collection issue

Register your root namespace to avoid MDS Garbage Collection to wipe it away during page transitions.

Type.registerNamespace('Client');

3. Addressing MDS Script Reload issue

Register your script to have MDS system reload it every time, even after a page transition.

var scriptUrl = _spPageContextInfo.siteServerRelativeUrl + "/SiteAssets/CSRScript.js";
RegisterModuleInit(scriptUrl, Client.Project.Functions.OverrideTemplates);

Append a revision number to the JSLink attribute of your custom field definition and update it at any script change/deploy in order to prevent browsers from using the old cached version.

<Field
      ID="{ce3d02df-d05f-4476-b457-6b28f1531f7c}"
      Name="MyFieldInternalName"
      DisplayName="My field display name"
      Type="Number"
      Required="FALSE"
      JSLink="~sitecollection/SiteAssets/CSRScript.js?rev=1.0.0.0"
      Group="My Custom Group">
</Field>

4. Making it work without MDS as well

If MDS is disabled the _spPageContextInfo object is undefined in the script inline code.
If that is the case there is no need to register your script and you can call the template override function directly.

if (typeof _spPageContextInfo != ‘undefined’ &amp;amp;amp;amp;&amp;amp;amp;amp; _spPageContextInfo != null) {
   Client.Project.Functions.MdsOverrideTemplates();
} else {
   Client.Project.Functions.OverrideTemplates();
}

References

Wictor Wilén

Martin Hatch

Technet