A Full-Fledged Client-side Rendering (CSR) Template, a Rating Field

In this post I’m going to write a more full-fledged CSR implementation, in that I’ll do the rendering and override template behaviors to deal with that rendering. It will be a simple rating field that let’s the user rate an item with a value from one to ten. I’m also going to override most of what you might want to override when implementing a display template to modify a form field, as in the NewForm, EditForm, DisplayForm, View, and OnPreRender callbacks plus Validation.
 
So far, all of the CSR display templates I’ve shown for modifying form fields have been fairly evil. At least from Microsoft’s point of view. The reason is that I haven’t actually rendered anything, I’ve let SharePoint do the rendering and then manipulated the DOM afterward. In general, Microsoft would say, if you didn’t render it, don’t touch it. I can see their point, but then again if SharePoint gets me 80% of my customers requirements and I have to hack it a bit to get the other 20%, I’m not too proud.


This post is part of a series on Client Side Rendering:
 
  1. Overview of SharePoint Client Side Rendering (CSR)
  2. Creating Cascading Lookups with SharePoint Client Side Rendering (CSR)
  3. Setting the JSLink Property of a Field Using JavaScript
  4. Creating an Autocomplete Client-side Rendering (CSR) Template
  5. Dynamic CSR (Client-side Rendering) Templates
  6. A Full-Fledged Client-side Rendering (CSR) Template, a Rating Field
  7. Tabbed Forms with Client-side Rendering (CSR)
  8. An Accordion View, Custom List View Display Templates (CSR)
  9. CSR on Steroids and the Amazing Technicolor Text Boxes

The code is just going to allow the user to enter a rating, as in a number from one to ten. And through CSS, it’s going to color-code the value. I’ve added a field called rating to the form, of type number. In the new and edit forms, it will be color-coded on the form load event for the initial value, and also re-colored on change (meaning on focus lost). In the display form and views, it will just get a color code when initialized with a value. The colors are green (for values 8-10), yellow (for values 7-6) and red (for anything below 6). So here is an edit form with the rating template:

Edit form with Rating field

And here is a view with the rating field:

View with Rating field

This template is going to be what I called a dynamic CSR template in my last post, meaning the code will be added to IntelliPoint_ClientTemplates.js, and administrators will be able to associate this CSR with Number fields using the browser by going to SetCSRConfig.aspx. Ideally, neither the overrides building code in IntelliPoint_ClientTemplates.js nor SetCSRConfig.aspx should have to change as I drop in new CSR templates. The reality is that I did need to make a couple of changes this time to accommodate things I didn’t need before, and they are:
 
  1. I implemented my OnPreRender to only get called back for a single field because on forms that’s usually what I want. But on views, OnPreRender is only called once, on the field LinkTitle, and that’s not a field I want to override. So I added OnPreRenderAll to my return from getClientTemplates to indicate I want to be called on every call to OnPreRender regardless of context, just like the OOB implementation. Because I do need to do something on pre-render in Views.
  2. The overrides building code creates a new empty IntelliPoint.clientTemplatesConfig object if it does not exist (i.e. the configuration file hasn’t been written yet), and if an implementation configuration is undefined it creates an empty array of field overrides.
With these changes, no changes were required in SetCSRConfig.aspx in order to accommodate the new template implementation.
 
Here is the code for the dynamic CSR template. The implementation’s for the three callbacks which will be called by SPClientTemplates have been removed and will be shown and described separately below.

If you’ve followed along with the series, this should look fairly familiar, so I’m just going to mention a couple things that are different from previous posts.
 
  • First, the implementation of the configure method is trivial, it just creates a node and calls the callback, because there is no implementation-specific configuration for this template.
  • For the same reason, canEdit is set to false, which means the edit button next to previously configured fields with this template will be disabled.
  • As previously mentioned, getClientTemplates overrides the NewForm, EditForm, DisplayForm, View, and on pre render methods, which is everything that can be overridden except on post render. The edit method will override both NewForm and EditForm, and the display method will override both the DisplayForm and View.
  • The on pre render handler uses the newly created OnPreRenderAll so it will get called back every time SPClientTemplates calls OnPreRender regardless of context. This means it will get called once for each field on forms, and once for LinkTitle on views. If it didn’t use OnPreRenderAll, it wouldn’t get called on views at all.
Below is the SetCSRConfig.aspx page with our new template added to the template implementations. Note the pencil icon is disabled because there is nothing to configure:

Edit form with Rating field

Here is the pre-render callback:

This method just prepends some CSS onto the body of the HTML document. A lot of samples, like those from Office365 Patterns and Practices, insert a link into the head and load their CSS from an external file, which technically is a best practice but it leads to a very jumpy form. The HTML loads and then sometime later the browser gets the styles and everything changes. By embedding the CSS in my JavaScript and then shoving it into the page, my styles are there before the HTML and it’s a much more pleasing experience for end users. And with the help of a trick from Andrey Markeev I embed the CSS as a multi-line comment, which makes it easy to do the CSS in a separate file with syntax highlighting and IntelliSense, and just copy it here when I’m done. If I have a build process like Node/Webpack, I could even automate keeping these in sync.
 
And here is the NewForm and EditForm override:

It does the following:
 
  1. First it constructs a couple of jQuery objects whose HTML will be returned from this function to be rendered later by SPClientTemplates.
  2. Then it initializes the input with a class obtained by the helper value2Class also shown above, which will be used by the display override as well.
  3. It then creates a deferred event handler to handle onchange events and update the input class based on the new value. Deferred event handlers are a nice way to handle these kind of things, because you can’t just attach an event handler directly since the controls aren’t in the DOM yet. The alternative is to attach an event handler later using either OnPostRender or registerInitCallback, but by doing it inline here, it creates a closure so the context that was passed into edit is available to the handler too. The context is somewhat different in OnPostRender.
  4. Since I’m doing the rendering, the form has no way of knowing how to get a value for my controls on submit, so I have to register a get value callback to do that.
  5. Then it calls registerValidation, which is not shown here. It is complicated enough to deserve it’s own section below.
  6. And finally, it returns the HTML that I want SPClientTemplates to render for this field.
Now here is the callback for DisplayForm and View:

Not much to this one. It just returns some HTML to view the field value, using the same value2Class method used in edit to determine what color the field should be. Keep in mind that the context is different at different points of the life cycle and also in different forms. The edit callback used formCtx.fieldValue to get the current value, but here I use the somewhat more complex syntax ctx.CurrentItem[ctx.CurrentFieldSchema.Name]. The reason is that formCtx.fieldValue will work fine for DisplayForm, but it is null on Views; ctx.CurrentItem is available on both forms and views.
 
And last, but certainly not least, is the registerValidation implementation:

And it does the following:
 
  1. If you do the rendering, you almost certainly need to call registerValidationErrorCallback. SharePoint doesn’t know what your field looks like, so it doesn’t know where to put error messages. If you don’t do this, validation errors will prevent the form from being saved, but no error message will be displayed explaining why.
  2. Ultimately, we’re going to call registerClientValidator on the form context, and it takes in an instance of SPClientForms.ClientValidation.ValidatorSet, so we need to create one of them.
  3. Now we’ll create a custom validator to ensure the input is a number between 1 and 10 inclusive. A validator is just an object instance with a Validate method, which takes as it’s only argument the current value as a string. By creating this as an object literal, we create a closure so the form context is available to the validate method, which can be useful although we don’t need it here.
  4. There’s no such thing as taking partial responsibility for validation, it’s all or nothing. Which means if we don’t validate required, the field is not required, even if it is configured to be required through the list settings. Also, don’t just add a RequiredValidator all the time. If it’s there, the form will treat it as required even if it’s not configured that way through the list settings. So add a required validator only if the field is configured to be required.
  5. Now that all of our validators are attached to the validator set, call registerClientValidator passing it our validator set.
I’ve now covered all of the override callbacks from my overview. I’ve also called the form context registered callbacks except registerFocusCallback and registerHasValueChangedCallback. And to be honest, I’ve never used either of these and have never seen any bad side effects caused by not using them. I guess someday I’ll have to go back to CSRSpy to try and reverse engineer what these are for, because I’m not holding my breath waiting for documentation. Anyway, if I want to be notified for focus events, I usually just attach my own deferred event handler. And while registerHasValueChangedCallback seems like it might be useful, I haven’t seen any documentation explaining when it gets called.
 
This pretty well wraps up what I wanted to cover regarding using Client-side Rendering to modify SharePoint forms. If I write any more posts about form templates, I’ll probably just describe what the thing does and attach the source with no detailed explanation of the code. Armed with the knowledge in these 6 posts, and the various posts I’ve referenced, you can now do pretty much anything you want with CSR form modifications, limited only by your knowledge and skill with HTML, CSS, and JavaScript. At some point I’ll write some more posts about View display templates, search display templates, and combining CSR with setting JSLink on content types.

Reference

#javascript multiline trick, Andrey Markeev

 
RatingCSR.zip – the complete source code.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to top