In a nutshell, MDS registers JavaScript files as it downloads/executes them, and on a subsequent page load, if the request calls for the same file, the MDS engine will check the list, see that it already has the script, and not download/execute it a second time. Thus, the CSR code is not run and the custom rendering is not applied.
There is a way around this. SharePoint provides a function called
RegisterModuleInit()
which you can use to tell MDS that a particular function from a particular file should always be executed. RegisterModuleInit()
takes two arguments – the path of the file that has the function to execute, and the name of the function. Now, let’s take a look at one of the popular patterns for constructing CSR template overrides, which happens to be the one I used up until this project:- (function () {
- var overrideCtx = {};
- overrideCtx.Templates = {};
- overrideCtx.Templates.Item = customItem;
- SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
- })();
- function customItem(ctx) {
- var itemHtml = "";
- // do whatever processing is necessary
- // here to generate the custom html
- return itemHtml;
- }
We can see two things here:
1. The template overrides are defined and registered with the template manager in an anonymous self-executing function, thus there is no named entry point to register with
RegisterModuleInit()
. In order to use RegisterModuleInit()
to get the code functioning properly with MDS, the template override code needs to be changed to have a named entry point.2. The
customItem()
function is defined in the global namespace. Keep in mind, for this project I was working with several template override files being loaded on the same page, many with multiple rendering override functions defined. If not handled correctly, the global namespace was going to get polluted.So, in order to fix both of these problems, I came up with the following pattern, which I now use for all of my CSR template override scripts:
- var DEC = DEC || {};
- DEC.thisOverride = (function () {
- function customItem (ctx) {
- var itemHtml = "";
- // do whatever processing is necessary
- // here to generate the custom html
- return itemHtml;
- }
- return {
- render: function () {
- SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
- Templates: {
- Item: customItem
- }
- });
- }
- }
- })();
- // register for MDS enabled sites
- RegisterModuleInit(SPClientTemplates.Utility.ReplaceUrlTokens("~site/SiteAssets/Scripts/myOverrideScript.js"), DEC.thisOverride.render);
- // fallback for non-MDS enabled sites
- DEC.thisOverride.render();
Let’s look at what’s going on in the code.
First, I register (or retrieve) my own namespace, so I can stay organized (line 1).
Second, I add a property
thisOverride
(line 3). I named it generically for this example, but the name of this property should reflect the specific SharePoint asset you are applying the override to. That way many overrides can co-exist on the same page within the custom namespace (e.g. DEC.calendarOverride
, DEC.contactsOverride
, DEC.customFieldXOverride
, DEC.customListYOverride
).I then use the revealing module pattern to create a closure in which I contain all the functions I might need for the custom rendering. By keeping those functions private, I can still use the same internal naming convention for the actual override functions (e.g.
customItem
, render
, etc.). The only publicly exposed member is the render
function which registers the overrides with the template manager, and becomes the named entry point.Lastly, I register the entry point function with the MDS system (line 23), and provide a fallback for non-MDS enabled sites (line 26), ensuring that the template overrides will get applied no matter what.
Notice also that the
SPClientTemplates.Utility
namespace offers a function to replace URL tokens, so you can use tokens when defining the path to your override file. This allows for much greater flexibility when including the CSR script as part of a feature that may be activated on a number of sites.So there you have it – a CSR template override pattern that is flexible enough to work on MDS enabled or disabled sites, while maintaining responsible use of the global namespace.