Introduction
In SharePoint 2013, Client Side Rendering (aka CSR) is used for rendering list views, list forms and search results. This article provides a head-start into the CSR API for list views: how CSR works, how you can use the API, and the code examples, those you can adapt for your own solutions.
How CSR works
Unlike previous SharePoint rendering systems (XSLT in 2010 and CAML in 2007), CSR is client-side. The only thing server-side webparts do is they put a huge bulk of raw data into the page, in JSON format. This data become JS object when the page loads, and from then on the Client Side Rendering begins.
So basically, what CSR does is it gets the JS object with raw data as input, and renders huge HTML string based on it:
After HTML string is fully formed, it is inserted into the DOM.
CSR processing of the raw data is divided into stages, each represented by one overridable JS function. Each function returns its own chunk of HTML. Stages are often nested into each other.
The stages order isn't documented, so I had to dive head first into the clienttemplates.debug.js file to figure it out. For list views, the order turns out to be the following:
Group, Item and Field functions can be called many times, while View, Header, Body and Footer are called only once.
Visually it looks like this:
Stage-functions can be overriden with custom functions, thus it really helps to have this picture around when planning customizations.
Also, there are two additional events that can be used for customization: OnPreRender and OnPostRender. These are not the part of the main process: they don't yield HTML chunks, but rather behave as ordinary events, so you can subscribe to them and execute some code at a specific moment.
OnPreRender and OnPostRender events are shown on the full CSR diagram below:
As you can see, OnPostRender is particularily useful, because it fires after the formed HTML string is inserted into DOM, so that this is the earliest moment when you can manipulate the DOM elements that were rendered by CSR.
Using CSR API
Main entry point of the CSR API is the following function:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(options)
The options parameter is a JS object that contains all the necessary information for customizations. It has the following core structure:
var options = {
OnPreRender: ,
Templates: {
View: ,
Body: ,
Header: ,
Footer: ,
Group: ,
Item: ,
Fields: {
'Field1 Internal Name': {
View: ,
EditForm: ,
DisplayForm: ,
NewForm:
},
'Field2 Internal Name': {
View: ,
EditForm: ,
DisplayForm: ,
NewForm:
},
}
},
OnPostRender:
};
As you can see, this structure resembles the CSR stages and events.
Now, if I want to subscribe for the OnPreRender and OnPostRender events, I do it like this:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPreRender: function() { console.log('CSR OnPreRender'); },
OnPostRender: [
function() { console.log('CSR OnPostRender'); },
function() { alert('CSR OnPostRender'); }
]
});
OnPreRender and OnPostRender accept either a function or an array of them. All provided functions will be executed whenever event is fired.
Stage-related fields inside Templates
field accept a function or a string. For example, let's add some text to the footer of the list view:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: "Hello world from <#= ctx.ListTitle #>!"
}
});
Notice the <#= ... #>
tag inside the string! It automatically gets replaced with appropriate value from the context object. Context object ctx
is the very object with raw data that was rendered by server-side webparts.
Result of applying this customization:
As you can see, the Hello world string appeared in the footer of the list view, and also the token got replaced with the actual value (i.e. the list title).
Instead of string, function can be used for creating the same customization:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: function(ctx) {
return "Hello world from " + ctx.ListTitle + "!";
}
}
});
So as you can see, the function should accept the context object as the only parameter, and return the HTML string. In most cases, context object is the only parameter, but rarely, there might be additional parameters. For example, Group template receives 7 arguments.
Notice that we cannot use the <#= ... #>
tokens any more, at least directly.
As you might expect, there is a lot of string generation happening in CSR, thus I find it very convenient to leverage ASP.Net Ajax String.format function for that purpose. It has same syntax as the well known String.Format from C#. ASP.Net Ajax library is deployed on every SharePoint page by default through the ScriptManager control, so you don't have to worry about that.
Previous example becomes a bit cleaner with String.format:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: function(ctx) {
return String.format("Hello world from {0}!", ctx.ListTitle);
}
}
});
Now, please don't forget that RegisterTemplateOverrides call causes out-of-the-box function to be replaced by the custom function. So if you replace something, you have to understand that some functionality may be broken.
For example, if instead of Footer
, we use Body
in the previous code snippet, the result gets quite ugly:
So even if you want to replace only tiny part of the view, the limitedness of granularity provided by SharePoint may force you to end up with big amounts of code. Usually I have to copy-paste the corresponding OOTB code from clienttemplates.debug.js, and then modify it slightly.
By the way, even although Footer was initially empty in my particular case, it is normally used for paging, so if you redefine it unthinkingly, the paging functionality will disappear from your list!
One obvious solution to such situations is to use OnPostRender
event and perform customizations by manipulating the just rendered DOM elements, e.g. by using jQuery or HTML5 selectors.
Calling nested templates
Now, what happens if you want e.g. manipulate order of items, not touching the items themselves?
It turns out, that during the rendering process, CSR uses the ctx
object for passing some additional information to the inner functions. Including the references to all of the nested methods!
So using these references, you get ability to replace top CSR templates such as View, Body and Item without replacing the subtemplates.
Primitive example code for the View template:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
View: function(ctx) {
return ctx.RenderHeader() + ctx.RenderBody() + ctx.RenderFooter();
}
}
});
Example: Color coding
Let's consider a more real-world example - color coding. So for example, I want to paint green all rows from a document library that have status "Approved".
Since there's not much of a customization here - I need just to change a css property of an existing element, - best way to accomplish this would be to use OnPostRender
event and manipulate DOM elements that were rendered by CSR.
From OnPostRender
, the ctx
is accessible, thus it is not a problem to determine which documents were approved (given the Approved column is included into the view). The only tricky thing is to get the ID of the element that represents the row. Here I cheated a bit, by peeking into the CSR guts and finding a function, that generates row ids (but obviously, you can also figure out these IDs from the page source and generate them manually).
Now everything is simple: we have the element IDs, and we can acquire field values from ctx
(after a short investigation I found them in the ctx.ListData.Row
array).
So here's the final code:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
var rows = ctx.ListData.Row;
for (var i=0;i<rows.length;i++)
{
var isApproved = rows[i]["_ModerationStatus"] == "Approved";
if (isApproved)
{
var rowElementId = GenerateIIDForListItem(ctx, rows[i]);
var tr = document.getElementById(rowElementId);
tr.style.backgroundColor = "#ada";
}
}
}
});
This will work for English portal, but better make it international. For that, you need to replace "_ModerationStatus"
with "_ModerationStatus."
(dot at the end!), and compare with 0
.
As you can see, SharePoint and magic are still in the same tight embrace
But it works:
Example: Item link
By default, list item title links lead to view forms. What if we need them to point to edit forms?
This can be done easily with CSR, by redefining the LinkTitle field for the view.
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Fields: {
'LinkTitle': {'View':function(ctx) {
var url = String.format('{0}&ID={1}', ctx.editFormUrl, ctx.CurrentItem.ID);
return String.format('<a href="{0}" onclick="EditItem2(event, \'{0}\');return false;">{1}</a>', url, ctx.CurrentItem.Title);
}}
}
}
});
EditItem2
function is a global helper function that processes list item edit links in SharePoint. It is not mandatory, but I prefer use OOTB SharePoint functions wherever possible, because otherwise there is a chance to break some existing functionality.
ctx.editFormUrl
is the URL of the edit form, and ctx.CurrentItem
is data for the item that is being processed by the handler.
As you might have suspected, the contents of ctx
aren't documented. Simplest way to assess the various fields of the ctx
object is to put a breakpoint into your code and then use Watch window.
Result of performing the edit link customization of a Tasks list on my portal is shown on the following screenshot:
Here PageType=6
represents PAGE_EDITFORM
mode, as you can verify yourself on MSDN. And indeed, the link now leads straight to the list item edit form.
Applying customizations
Ok, now you know how to write the code, but how to actually apply the created customizations to the lists?
Piece of cake:
- Switch the page into edit mode
- Add Script Editor webpart right below the list view web part.
- Put your code into the Script Editor
- Save the page
That's it.
But of course, it's not the only way. In fact, you can put the code into the page by using any method you want - masterpage, page layout, script link, etc., just don't forget to wrap it into the SP.SOD.executeFunc
call, to ensure that the clienttemplates.js
file is loaded before your script is executed.
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
});
})
Targeting the list view
If you have more than one list view on the page, or if you're deploying the script throughout your site rather than to only one page, you might want to ensure, that the customizations are applied to the correct list view.
For that, you can use three additional fields of the options
object:
ListTemplateType
- ID of the list template. ViewStyle
- ID of the view style. BaseViewID
- BaseViewID of the list view.
ListTemplateType
For example, if you want to customize the out-of-the-box Tasks lists, use this code:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Footer: function(ctx) {
return String.format("Hello world from {0}!", ctx.ListTitle);
}
},
ListTemplateType: 171
});
IDs of standard list templates can be found on MSDN. Also custom list template IDs (10000 and above) can be used for lists based on such templates.
ViewStyle
In truth, not every SharePoint developer even knows what is view style. For simple reason: until now, they weren't really useful
View style of a view can be found and edited on the Modify View page:
Out-of-the-box view styles are dull and ugly. Nobody uses them. From developer's point of view, they're not quite appealing either: there's no supported way of changing those captions, and there is no supported way of adding your own styles.
But at least you can now conveniently override how they look. Hopefully MS will provide a way to change the captions too, somewhere in future.
So to override a view style, you need it's ID. It can be acquired from the page source:
BaseViewID
BaseViewID doesn't appear in UI. You can access it in SharePoint Designer, in Schema.xml or programmatically. It is an attribute of the View element.
By the way, do you even know what BaseViewID is? I have it explained here on SharePoint SE.
Thus, using ListTemplateType
, ViewStyle
and BaseViewID
, it is possible to target your customizations with some sort of reliability.
Briefly about JSLink
I find it very embarrasing when some popular bloggers use CSR and JSLink interchangeably.
Please remember, JSLink and CSR are completely different things, and they can very well live without each other.
Above I created CSR customizations that don't use JSLink and work fine.
JSLink is just a way to stick a javascript file to a SharePoint object. From that moment on, wherever this object appears, the script will be deployed as well. Script definitely can be non-CSR, e.g. jQuery, knockout.js, etc.
JSLink property was added to the following SharePoint objects in SP2013:
- SPContentType
- SPField
- SPForm
- SPView
- XsltListViewWebPart
- ListFormWebPart
- ... and some others
So indeed, it is very convenient to use JSLink in conjunction with CSR, because the CSR script gets the ability to "travel" wherever the list view is deployed.
Another interesting detail, is that you can deploy several scripts at once using the JSLink property. Just separate them with "|". This is a very good way to include your CSR dependencies along with the main script.
Conclusion
CSR is the main rendering framework in SharePoint 2013 and thus an essential part of the system. Knowing how it works and how to use it definitely pays off.
Benefits:
- Very easy to learn. You can start creating customizations literally in 10 minutes
- Client side => less server load
- CSR is javascript-based. Javascript community is huge and the number of different new JS libraries is astonishing.
- Works with apps, including sharepoint-hosted
- Huge improvement over the XSLT in terms of readability, performance, debugability, etc.
Disadvantages:
- Scarcely documented
- Sometimes requires magic skills :)
- Unfortunately, still a long way from modern rendering frameworks, such as KnockoutJs, AngularJs, etc.
Please note, in this article, I only explained CSR for list views. CSR is also used for displaying Quick Edit, search results and list forms. But each of these is a subject of a separate story.
I wrote series of articles regarding CSR for list forms, so if you do list forms customizations, have a look:
So good luck, and don't hesitate to drop a comment below if you have any questions!