Using multiple CSR Template Overrides on one Page

SharePoint 2013 ClientSideRendering is a great tool to customize list rendering. But when you put two or more ListViewWebParts on one page and want to use csr, things get complicated. This is because the registered template overrides are executed on all lists, even when you use JS-Link option in WebPart properties.
Microsoft tries to solve this problem by filtering the template overrides by ListTemplateType, BaseViewID and ViewStyle. But when you set up two (or more) custom lists via frontend, the all get ListTemplateType 100. And what happens, when you use two times the same list on one page…?
Now, after more than 2,5 years it was time to solve this problem. (Here is another solution: [1]).

Idea of CSR Framework to support multiple template overrides

My solution is to write one general template override which gets handed over multiple template override objects with filters. By evaluating the filters, the single general template override can decide if the code in an overidden object has to be executed or the default renderer has to be used.

Example: Footer Template Override

I will demonstrate the technique on the footer template. Here is a footer template override:

var leftWebPart_Override = {	
	Templates: {
		Footer: function(ctx) {
			return "Custom WP Footer 1";
		},
	}
};

As you see, this is standard SharePoint notation.
Then I define a CTXFilter:

var leftWebPart_CTXFilters = {		
	ListTitle: "CSR-Test-List"
};

One of my test list has the title ‘CSR-Test-List’.
Now, I register the override using my AtiaNuur.CSR Framework and initialize it:

AtiaNuur.CSR.RegisterTemplateOverrides(rightWebPart_Override, rightWebPart_CTXFilters);
AtiaNuur.CSR.init();

The output is something like this:
AtiaNuur.CSR
You see the customized WebPart on the left and a second uncustomized WebPart on the right. Both are custom lists (with ListTemplateType 100).

AtiaNuur.CSR Framework in detail

So what happens in AtiaNuur.CSR. The framework follows three steps. It doesn’t matter if it’s footer-, header-, body-, OnPreRender, fields- etc. override. Therefore I’ll demonstrate it on the footer template override. You can get the rest out of the code. These steps are:

  1. Filling an array with objects of render-function and filters
  2. Filtering on current context
  3. Calling rendering function

I’ll start in reverse order, as the rendering function is the most interesting and the rest will be easier to understand.

Calling rendering function (templateRenderer)

The CSR Object has two public methods:

  • RegisterTemplateOverrides(templateOverride, ctxFilters): which takes a standard SharePoint template override and CTX Filters.
  • init(): Doesn’t take any arguments. Finally registers the single general template override to the SharePoint SPClientTemplates.TemplateManager.

Let’s have a look at this single general template override. The basic construct is like this (here only for footer template):

var templateOverride = {
	Templates: {
          	Footer: function(ctx) { return templateRenderer(ctx, footers, RenderFooterTemplate); }
     	}
};

You this that it’s always called the templateRenderer function, which takes:

  • ctx: the context
  • footers: an array of objects with footer functions (or a return string) and filters
  • RenderFooterTemplate: SharePoint default rendering function for footer

Now, the templateRenderer iterates over all footers and determines, if the current function in the footer has to be executed or not. This works this way:

function templateRenderer(ctx, templateDefinitions, DefaultTemplate) {
	var returnHTML = null;
		
	templateDefinitions.forEach(function(templateDefinition) {
		if(renderingOnCTX(templateDefinition.CTXFilters, ctx)) {
			if(typeof templateDefinition.Renderer === "string")
				returnHTML = templateDefinition.Renderer;
			else if(typeof templateDefinition.Renderer === "function")
				returnHTML = templateDefinition.Renderer(ctx);
		}
	});
		
	if(returnHTML === null)
		returnHTML = DefaultTemplate(ctx);

	return returnHTML;
}

Filtering on current context (renderingOnCTX)

The renderingOnCTX function determines if the rendering function (templateDefinition.Renderer) has to be executed in current context or not. It simply does this by comparing any parameter in CTXFilters with corresponding parameter in ctx. So you can compare ListTitle, URLs and also WebPart-Number. It is also possible, to execute custom function, by adding them to the CTXFilter object.
Here is the renderingOnCTX function:

function renderingOnCTX(ctxFilters, ctx) {
	var renderingOnCTX = true;
	var ctxFilterNames = Object.getOwnPropertyNames(ctxFilters);
	ctxFilterNames.forEach(function(filterName) {
		if(typeof ctxFilters[filterName] !== "function" && ctx.hasOwnProperty(filterName))
			renderingOnCTX = renderingOnCTX && ctx[filterName] === ctxFilters[filterName] 		
		else if(typeof ctxFilters[filterName] === "function")
		{
			renderingOnCTX = renderingOnCTX && ctxFilters[filterName](ctx);
		}
	});
	return renderingOnCTX;
}

And this is a filter definition with a ctx property comparison and a custom function:

{		
	ListTitle: "Documents",
	FilterFunction: function(ctx) {
		return (ctx.ListTitle.length > 1);
	}
}

You can define as many filter functions as you like. Also the name can be whatever you like.

Filling an array with objects of render-function and filters (RegisterTemplateOverrides)

The last part is now to get create the footers array of objects. This happens in the AtiaNuur.CSR.RegisterTemplateOverrides function (I’m showing just the footer part):

function RegisterTemplateOverrides(curTemplateOverride, ctxFilters) {
	if(curTemplateOverride.Templates !== undefined) {
		if(curTemplateOverride.Templates.Footer !== undefined) {
			footers.push({CTXFilters: ctxFilters, Renderer: curTemplateOverride.Templates.Footer});
		}
	}
}

As you see I create each footers object out of the ctxFilters and the Footer function.

And the other template overrides?

All other template override functions follow the same principle as the footer. The Templates.Fields object has been a little bit trickier, but it also works the same way.

How to use

Add the atianuur.csr.js to your page using masterpage, JS-Link or any other way you like. I used the masterpage, and also added a csr.js file which defines the concrete template override:

<SharePoint:ScriptLink language="javascript" name="~sitecollection/Style Library/atianuur.csr.js" OnDemand="false" runat="server" Localizable="false" LoadAfterUI="false" />
<SharePoint:ScriptLink language="javascript" name="~sitecollection/Style Library/csr.js" OnDemand="false" runat="server" Localizable="false" LoadAfterUI="true" />

I uploaded both files to the style library (any other folder works as well). Then I load the AtiaNuur.CSR framework before and the csr.js file after the UI.

And here is some code out of the csr.js file, which hands over a standard template override and a CTXFilter object:

var leftWebPart_Override = {	
	Templates: {
		Header: "",
		Footer: function(ctx) {
			return "Custom WP Footer 1";
		},
		Fields: {
			"LinkTitle" : {
				View: "This is not the original link text"
			}
		}
	},
	OnPreRender: function(ctx){ alert("I'm prerendering"); }
};
var leftWebPart_CTXFilters = {		
	ListTitle: "CSR-Test-List"
};
AtiaNuur.CSR.RegisterTemplateOverrides(leftWebPart_Override, leftWebPart_CTXFilters);

In this example, I also overwrite the LinkTitle-Field and the OnPreRender function.

Disclaimer

The code to get the default renderer comes from Jim Brown [2].

Sources

And here are the complete sources: atianuur.csr.

References

[1]: http://www.myfatblog.co.uk/index.php/2013/09/listview-web-part-issues-with-jslink-and-display-templates-a-solution/
[2]: http://sharepoint.stackexchange.com/questions/112506/sharepoint-2013-js-link-return-default-field-rendering

Debugging JavaScript that executes on Ribbon Button clicks

When you click a button in the ribbon, dynamics uses ajax to load the file where the js function is in (only once). Therefore you cannot set a breakpoint on the function, before the button click. Here I will present some solutions to debug such functions anyways:

  1. Set incorrect library in Ribbon definition
  2. Use “debugger;” statement
  3. Throw an error
  4. Use sourceUrl property

Set incorrect library in Ribbon definition

Interestingly dynamics loads the file where the function for the ribbon is in at page load. So the file is there, but won’t be used. By putting an incorrect library in the ribbon definition the ajax call will result in an 401 error. Luckily the function from the already loaded file will be used.
In this solution you can set a breakpoint before clicking on the button. Unfortunately this will also result in an error screen by dynamics.
Works in FF, Chrome. Not tested in IE.

Use “debugger;” statement

When you put

debugger;

into your sourcecode the debugger will pause on this line.
Works in Chrome. Not tested in IE.

Throw an error

You can also throw an catch an error in your code:

        try
        {
            throw new Error('breakpoint');   //debugger will break here
        }
        catch (e)
        {
        };

Then set your debugger to pause on all errors.
Works in Chrome, but not in FF. Not tested in IE.

Use sourceUrl property

By putting a

//@ sourceURL=filename.js

property into your javascript file, it will be listet as file in Chrome debugger, after it has been loaded. As it will be listet after you have clicked the button, you can only set a breakpoint after execution of the js function.
Works in Chrome, but not in FF. Not tested in IE.