Showing posts with label code patterns. Show all posts
Showing posts with label code patterns. Show all posts

Sunday, May 1, 2016

A Better Pattern for Client Side Rendering Scripts

Recently I did some work for a client that required combining multiple List View Web Parts on one page, and using Client Side Rendering (CSR) to apply custom styling to each of the the various views. The site was a Team Site that had Minimal Download Strategy (MDS) enabled, and this led to some interesting challenges. The MDS feature is supposed to speed up browsing by having the client only process the differences between the current page and the new page. However, this can lead to situations where custom CSR scripts do not get executed correctly.

In a nutshell, MDS registers JavaScript files as it downloads/executes them, and on a subsequent page load, if the request calls for the same file, the MDS engine will check the list, see that it already has the script, and not download/execute it a second time. Thus, the CSR code is not run and the custom rendering is not applied.

There is a way around this. SharePoint provides a function called RegisterModuleInit() which you can use to tell MDS that a particular function from a particular file should always be executed. RegisterModuleInit() takes two arguments – the path of the file that has the function to execute, and the name of the function. Now, let’s take a look at one of the popular patterns for constructing CSR template overrides, which happens to be the one I used up until this project:

  1. (function () {
  2.     var overrideCtx = {};
  3.     overrideCtx.Templates = {};
  4.     overrideCtx.Templates.Item = customItem;
  5.  
  6.     SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
  7. })();
  8.  
  9. function customItem(ctx) {
  10.     var itemHtml = "";
  11.     // do whatever processing is necessary
  12.     // here to generate the custom html
  13.     return itemHtml;
  14. }

We can see two things here:

1. The template overrides are defined and registered with the template manager in an anonymous self-executing function, thus there is no named entry point to register with RegisterModuleInit(). In order to use RegisterModuleInit() to get the code functioning properly with MDS, the template override code needs to be changed to have a named entry point.

2. The customItem() function is defined in the global namespace. Keep in mind, for this project I was working with several template override files being loaded on the same page, many with multiple rendering override functions defined. If not handled correctly, the global namespace was going to get polluted.

So, in order to fix both of these problems, I came up with the following pattern, which I now use for all of my CSR template override scripts:

  1. var DEC = DEC || {};
  2.  
  3. DEC.thisOverride = (function () {
  4.     function customItem (ctx) {
  5.         var itemHtml = "";
  6.         // do whatever processing is necessary
  7.         // here to generate the custom html
  8.         return itemHtml;
  9.     }
  10.  
  11.     return {
  12.         render: function () {
  13.             SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
  14.                 Templates: {
  15.                     Item: customItem
  16.                 }
  17.             });
  18.         }
  19.     }
  20. })();
  21.  
  22. // register for MDS enabled sites
  23. RegisterModuleInit(SPClientTemplates.Utility.ReplaceUrlTokens("~site/SiteAssets/Scripts/myOverrideScript.js"), DEC.thisOverride.render);
  24.  
  25. // fallback for non-MDS enabled sites
  26. DEC.thisOverride.render();

Let’s look at what’s going on in the code.

First, I register (or retrieve) my own namespace, so I can stay organized (line 1).

Second, I add a property thisOverride (line 3). I named it generically for this example, but the name of this property should reflect the specific SharePoint asset you are applying the override to. That way many overrides can co-exist on the same page within the custom namespace (e.g. DEC.calendarOverride, DEC.contactsOverride, DEC.customFieldXOverride, DEC.customListYOverride).

I then use the revealing module pattern to create a closure in which I contain all the functions I might need for the custom rendering. By keeping those functions private, I can still use the same internal naming convention for the actual override functions (e.g. customItem, render, etc.).  The only publicly exposed member is the render function which registers the overrides with the template manager, and becomes the named entry point.

Lastly, I register the entry point function with the MDS system (line 23), and provide a fallback for non-MDS enabled sites (line 26), ensuring that the template overrides will get applied no matter what.

Notice also that the SPClientTemplates.Utility namespace offers a function to replace URL tokens, so you can use tokens when defining the path to your override file. This allows for much greater flexibility when including the CSR script as part of a feature that may be activated on a number of sites.

So there you have it – a CSR template override pattern that is flexible enough to work on MDS enabled or disabled sites, while maintaining responsible use of the global namespace.

Friday, March 29, 2013

Interesting code usements, and structure:
Usement 1 - safely allowing unsafe updates

This is intended to be an ongoing series where I post code patterns that I find interesting.  Vets may find what I post here to be old hat, and newbs may find something useful.  Regardless, these are patterns that I have personally found to be interesting, for whatever reason.  If you have a helpful comment that will elaborate on what's here and educate me, bring it on.  My ultimate goal is to become a better coder.

The first code pattern in this series has to do with SharePoint, and the SPWeb.AllowUnsafeUpdates property.

AllowUnsafeUpdates = true.  We've all done it, despite the fact that it's there to protect us from cross-site scripting attacks.  There are just times when you want to update something and AllowUnsafeUpdates is set to false.  It happens.  And you need to get around it.

EDIT: Scott Brickey (see comments below) wanted me to remind all you SharePointers out there that changing the AllowUnsafeUpdates is only necessary when updating data in an HTTP GET request.  Any other time you are updating data, you should be able to do it without changing AllowUnsafeUpdates.

This is a pattern that leaves me feeling a little more reassured about protecting the safety of the SPweb object during an "unsafe" update.  The trick is to put your updating code in a try{} block, and whether or not you attempt to catch any exceptions, use a finally{} block to reset AllowUnsafeUpdates to false.  That way, if something goes wrong, at least your web object is re-protected.

EDIT: Thanks to an anonymous commenter for pointing out that the AllowUnsafeUpdates value does not persist outside the scope of the instance of SPWeb that you're working on.  So if you are not doing anything else with the SPWeb after you finish your updates, you don't need to set AllowUnsafeUpdates = false.

Here's an example:

using (SPSite site = new SPSite("http://asharepointsite"))
using (SPWeb web = site.OpenWeb())
{
    // do most of the work out here

    try
    {
        web.AllowUnsafeUpdates = true;

        // do the minimum amount of work that
        // necessitated allow unsafe updates

    }
    finally
    {
        web.AllowUnsafeUpdates = false;
    }

    // If you are still going to use your
    // SPWeb object here and do more work,
    // you want to use that finally above to
    // set AllowUnsafeUpdates to false.
    // But, if you are done with the object and
    // are going to let the SPWeb go out of scope
    // and get disposed, you don't need to worry
    // about setting it to false.
}

(Because of the using block, the web and site objects will be disposed of properly even if an exception is thrown, so you don't need to worry about that.)