Client Side Rendering with Async dependencies
By Anatoly Mironov
Yesterday I asked a question on SharePoint StackExchange:
I also asked Elio Struyf on Twitter: https://twitter.com/eliostruyf/status/540473976255152128 Good idea, Elio Struyf! Now I want to try it out.
Preparations
In this case I’ll be using my example from my blog post yesterday: Drag and Drop Image using Client Side Rendering I have created a new list and added a lookup field to my previous list. What I get is a Title of the lookup item, but not my custom field called DragAndDrop. In my test I will try to load the DragAndDrop Image using an ajax call and rendering it after Client Side Rendering is done with my item. To be complete, I want to show some screenshots for my lookup field: It will result in this OOTB rendering:
Trying out CSR with async dependencies
While working with jslink, first of all I want to show a loading image instead of an empty html element, to show that something is loading: Here is the skeletton of the jslink file: [code language=“javascript”] (function () { ‘use strict’; var display = function (ctx, field) { var containerId = "tolle"; var loadingImg = _spPageContextInfo.webAbsoluteUrl + "/_layouts/images/loadingcirclests16.gif"; //unfortunately SP.Utilities.Utility is not defined at this stage //SP.Utilities.Utility.getImageUrl("loadingcirclests16.gif"); return [’<div id="’, containerId, ‘"><img src="’, loadingImg, ‘"/></div>’].join(’’); }; var overrideContext = {}; overrideContext.Templates = overrideContext.Templates || {}; overrideContext.Templates = overrideContext.Templates || {}; overrideContext.Templates.Fields = { ‘TolleLookup’: { ‘DisplayForm’: display } }; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext); })(); [/code]
Making an ajax call
Next step is to initiate an ajax call. I am trying to avoid the jQuery dependency. There is so much you can do with the built-in javascript functions. For making an ajax call I am using SP.RequestExecutor.js Here is the result: The code should be quite self explaining: [code language=“javascript”] (function () { ‘use strict’; var onDataRetrieved = function(response) { console.log("yippie"); var data = JSON.parse(response.body); var imgSrc = data.d.DragAndDropImage; var container = document.getElementById("tolle"); container.innerHTML = [’<img src="’, imgSrc, ‘"/>’].join(’’); } var onError = function(response) { console.error("failed", response); } var initiateAjaxCall = function(ctx) { var item = ctx.CurrentItem; var fieldName = ctx.CurrentFieldSchema.Name; var fieldValue = item[fieldName]; var itemId = fieldValue.split(";#")[0]; var lookupListId = ctx.ListSchema.Field[0].LookupListId; var url = [window._spPageContextInfo.webAbsoluteUrl, "/_api/web/lists/getbyid(’", lookupListId, "’)/Items(", itemId, ")?$select=DragAndDropImage"].join(""); SP.SOD.registerSod(‘sp.requestexecutor.js’, ‘/_layouts/15/sp.requestexecutor.js’); SP.SOD.executeFunc("sp.requestexecutor.js", "SP.RequestExecutor", function () { var executor = new SP.RequestExecutor(window._spPageContextInfo.webAbsoluteUrl); executor.executeAsync( { url: url, method: "GET", headers: { "Accept": "application/json; odata=verbose" }, success: onDataRetrieved, error: onError } ); }); } var display = function (ctx, field) { if (!ctx.CurrentItem[ctx.CurrentFieldSchema.Name]) { //if there is no value return “”; } var containerId = "tolle"; var loadingImg = window._spPageContextInfo.webAbsoluteUrl + "/_layouts/images/loadingcirclests16.gif"; initiateAjaxCall(ctx); //unfortunately SP.Utilities.Utility is not defined at this stage //SP.Utilities.Utility.getImageUrl("loadingcirclests16.gif"); return [’<div id="’, containerId, ‘"><img src="’, loadingImg, ‘"/></div>’].join(’’); }; var overrideContext = {}; overrideContext.Templates = overrideContext.Templates || {}; overrideContext.Templates = overrideContext.Templates || {}; overrideContext.Templates.Fields = { ‘TolleLookup’: { ‘DisplayForm’: display } }; SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext); })(); [/code]
Next steps and further considerations
It seems to work, although I have some considerations:
- What happens if the data from the ajax call is retrieved before Client Side Rendering is done (then the container is not rendered yet). Even if the risk for that is low, it should be handled properly.
- It would be good to have a consistent look and feel in the Display form and in the list view. To make it possible, following should be done:
- We must create references to html elements (containers) with unique ids, need to implement logic for generating ids and keeping track of the right elements.
- Ajax calls should be bundled, otherwise it will hugely impact the performance, even in a list view with 30 items.