Introduction
SharePoint CSR for List Forms has two modes: Standard and CustomLayout. Unfortunately, the latter which is intended for forms with custom layouts, doesn't work (and btw, this is still not fixed in SP2016).
I came up with a small JS code snippet that solves this problem and enables creating completely custom form layouts (this works in Standard mode, no need to switch into CustomLayout
).
Usage Example
This is the form that will be customized:
And this is the template (it is added to the page via Script Editor):
<div style="display:none" data-role="form">
<h3>
<span data-field="Title">City of <strong>{Value}</strong></span>
(<span data-field="Country">{Value.split(';#')[1]}</span>)
</h3>
<hr>
<div data-field="*" class="my-formfield">
<label>{Title} <span style="{ Required ? '' : 'display: none' }">*</span></label>
<span data-role="field-control" />
</div>
</div>
Then, also on this page is included the 50-lines js file that you'll find below.
And here's the result:
There's also some minimal additional CSS that I added to the page along with the template itself:
<style>
.my-formfield {
padding-bottom: 4px;
}
.my-formfield > label {
font-size: 14px;
width: 115px;
display: inline-block;
vertical-align: top;
}
.my-formfield > span {
display: inline-block;
vertical-align: top;
}
</style>
Show Me the Code!
Ok, and now, here's the code that processes this template and does the customization:
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
function init() {
var templates = {};
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPreRender: function(ctx) {
if (!ctx.FormContext)
return;
var listId = ctx.FormContext.listAttributes.Id;
var templ = templates[listId] || document.querySelector
("[data-list-id='" + listId + "']") ||
document.querySelector("[data-role='form']");
if (!templ)
return;
if (!templates[listId])
{
var table = document.querySelector("#WebPart"+
ctx.FormUniqueId + " .ms-formtable");
table.style.display = 'none';
table.parentNode.insertBefore(templ, table);
templ.style.display = '';
templates[listId] = templ;
}
var field = ctx.ListSchema.Field[0];
var el = document.querySelector('tr > td > span#'+
ctx.FormUniqueId + listId + field.Name);
var target = templ.querySelector("[data-field~='" +
field.Name + "']") || templ.querySelector("[data-field='\*']");
if (target && el && field.Name != "Attachments")
{
if (target.attributes['data-field'].value=='*' ||
target.attributes['data-field'].value.indexOf(' ') != -1)
{
target.style.display = 'none';
var cloned=target.cloneNode(true);
cloned.setAttribute("data-field", field.Name);
cloned.style.display = '';
target.parentNode.insertBefore(cloned, target);
target = cloned;
}
var html = target.innerHTML;
field.Value = Encoding.HtmlEncode(ctx.ListData.Items[0][field.Name]);
html = html.replace(/{[^}]+}/g, function(m) { with (field) return eval(m); });
target.innerHTML=html;
var control = target.querySelector("[data-role='field-control']");
control && control.parentNode.replaceChild(el, control);
}
}
});
}
RegisterModuleInit(SPClientTemplates.Utility.ReplaceUrlTokens
("~siteCollection/Style Library/customlayout.js"), init);
init();
});
The code is pretty straightforward:
- Find the template
- Bring template next to original form
- Hide original form
- Show template
- Replace stuff in template
- Done
Please feel free to change the code to adapt it for your own needs! :)
"Installation"
Copy the code and include it to the page using any method you like. It is also safe to include it in the site scope (e.g. via masterpage or custom action) and use it along with any other CSR code.
If your site has MDS (Minimal Download Strategy) enabled, don't forget you cannot include JavaScript inline, as this breaks MDS. So create a separate js file, put code there, upload the file e.g. to Style Library, and include it to the page e.g. using JSLink of the ListFormWebPart
.
Template Documentation
Actually, you can integrate CSR with existing templating engine, but my idea was to create a snippet that doesn't have dependencies and which I could quickly copy-paste into the page and start working. Below is the documentation for that micro-engine.
So element with attribute data-role="form"
will be the template. Please also add style="display: none"
for better user experience, so that initially the template is hidden.
If you have several forms on one page, add data-list-id
attribute and put id of the list there:
<div style="display:none" data-role="form"
data-list-id="YOUR-LIST-GUID-HERE">
<!--
</div>
You can insert your HTML template to the page via Script Editor webpart, Content Editor Webpart with an external file as source, or any other way to bring a piece of HTML into the page.
Inside the form template, elements with attribute data-field
define scope of the corresponding field. You can also put several field names into one data-field
attribute, separated by whitespace (see tabs example below). Fields that are not specifically mentioned anywhere, will be rendered using data-field="*"
template.
Notice: Internal field name should be used to identify fields.
Element with attribute data-role="field-control"
will be replaced with the field control. The field control is rendered by CSR, so if you want to replace it with your custom control, you can do that using normal CSR approach.
Text in curly brackets will be evaluated against the corresponding field object, which looks like this:
{
Description: "",
Direction: "none",
FieldType: "Text",
Hidden: false,
IMEMode: null,
Id: "FIELD-GUID-GOES-HERE",
MaxLength: 255,
Name: "Title",
ReadOnlyField: false,
Required: true,
Title: "Title",
Type: "Text",
Value: "Tallin"
}
So you can refer to any of those properties inside curly brackets. Depending on the field type, there may be some additional properties in this object (or some properties may not be there).
Why Not Render the Form Completely From Scratch?
Comparing to rendering the form from scratch, this approach has some important benefits:
- Native SharePoint form functionality is retained (loading form data, saving, validation, ribbon integration, etc.)
- This method is CSR-compatible: you can use CSR for customizing field controls as before. The code just adds ability to change forms layout
- No external dependencies. Just copy-paste and it works.
- Extremely small amount of code.
Another Example: Tabs
Let's say we have a standard Contacts list. Now it's extremely easy to add tabs to Contacts list form.
CSS + template code + jquery ui dependencies:
<div data-role="form" style="display:none">
<div id="my-formtabs">
<ul>
<li><a href="#tab-general">General</a></li>
<li><a href="#tab-contact-info">Contact info</a></li>
<li><a href="#tab-other">Other</a></li>
</ul>
<div id="tab-general">
<div data-field="Title FirstName Company JobTitle"
class="my-formfield">
<label>{Title} <span style="{ Required ?
'' : 'display: none' }">*</span></label>
<span data-role="field-control" />
</div>
</div>
<div id="tab-contact-info">
<div data-field="Email CellPhone
WorkAddress WorkState WorkCountry" class="my-formfield">
<label>{Title} <span style="{ Required ?
'' : 'display: none' }">*</span></label>
<span data-role="field-control" />
</div>
</div>
<div id="tab-other">
<div data-field="*" class="my-formfield">
<label>{Title} <span style="{ Required ? '' :
'display: none' }">*</span></label>
<span data-role="field-control" />
</div>
</div>
</div>
</div>
<!--
<style>
.my-formfield {
padding-bottom: 4px;
}
.my-formfield > label {
font-size: 14px;
width: 115px;
display: inline-block;
vertical-align: top;
}
.my-formfield > span {
display: inline-block;
vertical-align: top;
}
</style>
<!--
<link rel="stylesheet"
href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script type="text/javascript">
document.write('<script
src="//code.jquery.com/jquery-1.10.2.js"><'+'/script>');
document.write('<script
src="//code.jquery.com/ui/1.11.4/jquery-ui.js"><'+'/script>');
</script>
<script type="text/javascript">
$('#my-formtabs').tabs();
</script>
Notice: Of course, if you have MDS-enabled site, you shouldn't use inline code.
The result:
Further Reading
Check out my other articles about CSR:
Conclusion
CSR is not perfect, but... it's JavaScript! So even if it doesn't support something, it's always possible to extend it and resolve the problem. 50 lines of code presented in this article enable custom layouts for CSR list forms.