Star Ratings Display Template, Advanced CSR

In this post, I’m going to do a CSR field rendering template for a star ratings field. It’s just what it sounds like, give the user an opportunity to rate something with 0 to 5 stars, by clicking into an image of 5 stars. Under the hood it will just be a numeric field, but as much as possible I’d like the user to never see the number. Anywhere the field appears, they should see an image with the appropriate number of gold stars.

I first presented this star ratings CSR template at SharePoint Saturday Twin Cities (#SPSTC) in 2016. It was one of my first SharePoint Saturdays, and my evaluations weren’t terrible, but they weren’t great either (average of 7.1, organizers would usually like to see at least an 8). I did get a lot of constructive suggestions. Mostly, they liked the demos, but thought I was a bit all over the place with the code. Not too surprising, it’s hard to do more than very simple code segments in a 50 minute time slot, and this sample is 150 lines (the other one is almost 4 times that, so I set aside 15 minutes for this one and 30 minutes for the other, which was tough). Point is, I like doing advanced stuff, but it isn’t easy and hopefully I’ve gotten better at it over time (my evals have gotten better anyway). But if you were there, consider this blog post my belated attempt to make amends. Someone suggested a video would be helpful; maybe someday I’ll do that too time permitting, but video tends to take me a long time.

One departure from my previous CSR templates is that this one is pure JavaScript with no external dependencies.

What are we Building?

The most complete example of CSR for form field rendering that I’ve done in these posts is the Ratings template. Complete, in that it overrode all of the rendering methods for forms and views, and registered most of useful callbacks that you can register, including validation. But it was rather simple. Enter a number 0 through 10 in a text box and it color coded the HTML. It demonstrated a lot of CSR points, but the user experience wasn’t that satisfying and this template will attempt to remedy that. You don’t go to Amazon and rate something by entering 0 through 5, you click on the stars and it shows as 1 through 5 stars. So we’re going to build something that mimics that behavior, a star ratings CSR template.

Here is a view showing some fields that have my star ratings CSR template applied to them:

A view with star ratings

fig 1 – A view with star ratings fields.

And here is a form:

A form with star ratings

fig 2 – A new/edit form with star ratings fields.

Now for starters, there is obviously some CSR going on here besides my field. Like it’s obviously using my Tabbed Forms template, but we’re just going to talk about the star ratings template in this post.

The display and view simply show the number of gold stars based on the current value. On the new and edit forms, if the user hovers over the stars the cursor changes to a hand, and if they click, an event handler calculates what star they clicked on from the current mouse position and changes the display to show that number of gold stars. Note that there is also a half star width area to the left of the first star on which the user can click to register 0 stars (there’s nothing I hate more than a review with the snarky comment I would have given 0 stars if I could).

My previous Ratings template also did custom validation, to produce an error if the user input was out of the range of 0 through 10. But I don’t need to do that here, because my input doesn’t allow them to put in an arbitrary integer that could be out of range. However, since I’m doing the rendering, SharePoint doesn’t know where to put validation error messages, so I do still need to handle that.

Now, as an added bonus, here is what it looks like in quick edit mode:

Quick edit view with star ratings

fig 3 – A quick edit view with star ratings fields.

Quick edit uses the same override method as any other view, the View method. If you double click on it, it reverts to whatever the out of box rendering is, in this case the rendering for a number. This is the one opportunity the user has to put in an out of range value. There’s still no reason to add custom validation to the CSR template though, because it won’t get called anyway. Once the user is editing in a quick edit field, CSR is out of the picture. So you still may need to code somewhat defensively, elsewhere in your template, to check and correct for out of range values. In this case, since it’s a number field, I could configure min and max values and 0 decimal places, and the OOB rendering would enforce my range too. But some clever person could still reconfigure the field later, and depending on your field type and range of acceptable values, it may not be so easy to lock it down in the OOB rendering.

The HTML and CSS

Before we dive into the JavaScript, I want to explain the HTML and CSS that’s going to make this work, just to get that out of the way. There are a number of ways you can make this work, but the way I’ve chosen starts with the following image:

An image with 6 rows of stars, representing 0-5 stars

fig 4 – The stars background image.

There is one row of stars for each possible value, each 20 pixels high and 110 pixels long (the stars are each 20×20, but I need an extra 10 pixels on the left to allow 0 stars to be selected).

The actual control is going to be a div, which I’ll confine to 20×110 (the size of one row in the image) and it will use the image above as it’s background image. Then when the user clicks on a star, I’ll slide the background to the appropriate row. This will all be done with CSS.

So here is the markup to my control:

It doesn’t take much, it’s just a div to which I add a couple of classes, set the id to something from which I can derive the internal name of the field, and also add a data attribute which holds the value. Technically, I could derive the value from the classes, but that’s a bit of a PITA so I assign the data value at the same time as I assign the classes to make it a bit easier.

And here is the CSS:

There isn’t a heck of a lot I’m going to say about the CSS, it’s pretty simple. But do note, that the actual data URI for the stars image is quite a bit bigger than what I’m showing here (you’ll see what I mean if you download the actual source code below). I’ve replace the stars PNG with a simple Hello World PNG in the above source. Having a page worth of base 64 encoded garbage in the middle of my blog post just doesn’t add that much value. Point is, I don’t want the image to be a separate asynchronous download either or the form will be a bit jumpy while loading, once it’s rendered everything else, and before it’s gotten the stars image. That’s why I embed the image in the CSS as a data URI. In truth, the image is a bit big (20k+ encoded) to be embedded this way, but I haven’t made much of an effort to optimize it yet, so I’m sure it could be quite a bit smaller.

The Star Ratings Template Implementation

And here is the implementation (or at least the implementation skeleton, I’ll do the rendering methods in a bit):

If you’re good at JavaScript and CSR, this shouldn’t look too confusing, especially with all those comments. If not, you might want to read some of the earlier posts in this series, I’m not going to try to repeat it all here. I’ll just hit the high notes; here is what is going on:

  1. First I create an array of field internal names. Just make this match the field internal names for the fields in your list to which you want to apply this template and you should be good to go.
  2. Next I create a instance that will hold my rendering implementation. At the moment it just contains a couple of empty members, which we’ll flesh out below.
  3. Next I create my overrides object, with an empty Template.Fields instance, and add an instance to this for each field I want to override. I’m overriding everything that can be overridden.
  4. Now I need to get my CSS injected into the page, and like the PNG I don’t want this to be a separate asynchronous download for the same reasons. So the CSS is just defined as a big string, which I get from a comment using the javascript multi-line trick I’ve shown in previous posts. Then I just shove it into the DOM as a script element. This is done OnPreRender, and I safeguard against potentially doing it more than once.
  5. Finally, I register my CSR template overrides, being careful to handle the MDS case.

And here is the display rendering method (used for DisplayForm and View overrides):

Not a lot to talk about here. I just create a new div element, set some properties on it, and return it’s source. Note that I use ctx.CurrentItem[ctx.CurrentFieldSchema.Name] instead of ctx.CurrentFieldValue, because the latter is only available on forms, not views. Also, I hate concatenating together big strings of HTML. It’s ugly and error prone. So I construct a new DOM element which I never actually inject into the DOM, that’s SPClientTemplate’s job. I then just return the outerHTML.

And here is the edit rendering method (used for NewForm and EditForm overrides):

As usual, there is a bit more going on here, but it’s not so complex. The rendering is almost identical to the view override with two small differences.

First, it adds a third CSS class to make the field appear editable. And B, it sets an onclick attribute, to a callback method in this instance called handleClickOnStarRating (which I’ll describe below). Since my element isn’t in the DOM yet, I can’t attach the handler dynamically. And since I’m not using jQuery or any other dependencies, I don’t have anything like deferred event handlers to work with. The only alternative approach I could have taken would be to add an OnPostRender (or use registerInitCallback), to find my element after it’s rendered and attach an event handler to it. I also create and return a span to hold validation error messages.

Then I register a get value callback. It just gets the div, pulls the current value from the data attribute, and returns it.

The last step before I return my HTML to be rendered is to register a validation error callback. This is passed an error message, which I shove into the span element I created earlier.

And here is the event handler implementation:

It just uses the horizontal offset of the mouse click relative to the horizontal offset of the div, and the known width of a star (20 pixels), to calculate which star the user clicked. It then adds the appropriate CSS classes to make the control display correctly, and sets the data attribute to the integer value. It uses the below helper to get the offsets in a manner a bit less unwieldy than the pure JavaScript way (and more jQuery-like). It also clears any validation error messages as soon as the user clicks on a star. And here is the getOffset helper:

Sum Up

So I haven’t actually done anything here that I didn’t do in at least one of the previous posts in this series, at least with regards to Client Side Rendering. It’s only more complex because the HTML and CSS is a bit more complex, but not that much either. With a little imagination, you can do some pretty fancy things with form rendering templates.

To get it deployed, just copy it to your style library and use the utility page I described in Setting the JSLink Property of a Field Using JavaScript to set the JSLink property on each of the fields to which you want to apply the star ratings rendering template. That script only works on site columns. If you want to do this with say a list column, you’ll need to rework it a bit. Once applied, your fields should work like what I described above in the ‘What are we Building?’ section. Here is what that utility page looks like for one of my fields:

Set jslink on field

There’s only one script to configure since I have no dependencies. If you’re placing your script in someplace other than the style library of the root web, you’ll need to adjust the path. And remember that JSLink does not work with resources that are outside of the current site collection, so the utility page will drop any script that doesn’t begin with ~sitecollection or ~site (and using ~site doesn’t make much sense unless you’re absolutely certain you’re only ever going to use this script in a single sub-site).

I have one other advanced template demo from #SPSTC that I’ll blog about next at some point. It’s an entity editor template (sort of people picker like) that can be applied to choice fields with an unwieldy number of choices to achieve a more pleasing user experience. It’s about four times as much code as this one, but I’ve been trying to blog pretty regularly, so hopefully it won’t be too long before I get around to it. – the source code

Leave a Comment