CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Tag Archives: async

Client Side Rendering with Async dependencies

Yesterday I asked a question on SharePoint StackExchange:

I also asked Elio Struyf on Twitter:

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:

csr-async-001csr-async-002

It will result in this OOTB rendering:
csr-async-003

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:
csr-async-004

Here is the skeletton of the jslink file:

(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);
})();

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:

csr-async-005

The code should be quite self explaining:

(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);
})();

Next steps and further considerations

It seems to work, although I have some considerations:

  1. 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.
  2. 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:
    1. 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.
    2. Ajax calls should be bundled, otherwise it will hugely impact the performance, even in a list view with 30 items.
Advertisements

Paging with JSOM

If there are many list items you try retrieve with javascript object model,paging could be very useful. Today I came across a wonderful blog post series about javascript object model in SharePoint: The SharePoint javascript object model – Resources and Real World Examples posted by David Mann and published on Aptilon Blog.

There is an example how to achieve paging with JSOM. The key is items.get_listItemCollectionPosition() and query.set_listItemCollectionPosition()

I have refactored David’s example to avoid global variables and to put into a module. Here is it. If you have a Tasks list in your site with many items, just hit F12 to open the console and paste this and see the result:

(function(SP) {
   var 
      ctx = SP.ClientContext.get_current(),
      list = ctx.get_web().get_lists().getByTitle('Tasks'),
      position,
      enumerator,
      view = '<View><ViewFields><FieldRef Name="Title"/></ViewFields><RowLimit>10</RowLimit></View>',
      query = new SP.CamlQuery(),
      items,
      init = function() {
         query.set_viewXml(view);			
      },
      loadChunks = function() {
         query.set_listItemCollectionPosition(position);
         items = list.getItems(query);
         ctx.load(items);
         ctx.executeQueryAsync(success, error);
      },
      success = function() {
         console.log("\nFound Matching Items! ");
         enumerator = items.getEnumerator();
         while(enumerator.moveNext()) {
            console.log("Title: " + enumerator.get_current().get_item("Title") );
         }
         position = items.get_listItemCollectionPosition();
         //when there are no items position is null
         position && loadChunks();
      },
      error = function(sender, args) {
         console.log('Request failed. Error: ' + args.get_message() + '. StackTrace: ' + args.get_stackTrace());
      };

   init();
   loadChunks();
})(SP);

Android: Asynchronously download images

In the Malmöfestivalen for Android where I participate we wanted to show thumbnails of events in lists. We have urls of thumbnails from Malmofestivalen API. This information is stored in a sqlite database and a SimpleCursorAdapter is used for getting the events from the database. It is possible to bind the urls to ImageView objects (by downloading the stream: 1, 2, 3). The problem is in a listactivity with many images it will freeze. So it must be an AsyncTask, and it should be some kind of local cache to avoid loading same images over and over again. Fortunately there is already ImageDownloader class provided in the android blog post: Multithreading For Performance .

The next step is to create custom adapter and extend from SimpleCursorAdapter: EventCursorAdapter, like described on Android-er and and SP-Technolab:

public class EventCursorAdapter extends SimpleCursorAdapter {
	private final ImageDownloader imageDownloader = new ImageDownloader();
	public EventCursorAdapter(Context context, int layout, Cursor c,
			String[] from, int[] to) {		
		super(context, layout, c, from, to);
		imageDownloader.setMode(ImageDownloader.Mode.NO_DOWNLOADED_DRAWABLE);
	}
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		View row = super.getView(position, convertView, parent);
		
		Cursor cursor = getCursor();
		cursor.moveToPosition(position);

		ImageView iv = (ImageView) row.findViewById(R.id.eventitemrowimage);

		String url = cursor.getString(cursor.getColumnIndex(EventProvider.EVENT_KEY_URISMALLIIMAGE));
		if (url.length() &gt; 0) {			
			imageDownloader.download(url, iv);
		}
		
		
		String start = cursor.getString(cursor.getColumnIndex(EventProvider.EVENT_KEY_STARTDATE));
		String end = cursor.getString(cursor.getColumnIndex(EventProvider.EVENT_KEY_ENDDATE));
		String dateString = DateHelper.createShortDateResume(start, end);
		TextView tv = (TextView) row.findViewById(R.id.eventitemrowtimeresume);
		tv.setText(dateString);
		return row;
	}
}

In this code the view is created by the super class and then it is modified, the alternative is to “inflate the view” completely self.

The result is an event list which opens directly and loads thumbnails asynchronously:

listactivity with images

 

XMLHttpRequest the hard way

$.ajax is great, it hides much of the complexity. But sometimes we need to work with “raw” javascript 🙂 So let’s look behind the scenes. The XMLHttpRequest (or just XHR) is used to open a connection to a server without a page reload. Internet Explorer calls it ActiveXObject and it differs in IE versions. Wikipedia article gives a good example how to create one constructor for all browsers:

if (typeof XMLHttpRequest == "undefined")
  XMLHttpRequest = function () {
    try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
      catch (e) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
      catch (e) {}
    try { return new ActiveXObject("Microsoft.XMLHTTP"); }
      catch (e) {}
    //Microsoft.XMLHTTP points to Msxml2.XMLHTTP and is redundant
    throw new Error("This browser does not support XMLHttpRequest.");
};

The remainder is more or less the same among the browsers. We open a connection defining the HTTP verb, URI and async mode (true or false):

var xhr = new XMLHttpRequest();
xhr.open("GET", "/_vti_bin/listdata.svc", true);
xhr.onreadystatechange = onStateChange;
xhr.send(null);

Pay attention to onreadystatechange (only lower case letters). If we choose async=false, the UI waits for the response which is not so kind to users, but maybe it is easier to write a program. Well, there is actually no option but to have async=true.

To provide the callback for success and error we can write the responding function onreadystatechange. This function will be called every time the state is changed. There are 5 states:

0 = uninitialized
1 = loading
2 = loaded
3 = interactive
4 = complete

The state 4 “complete” and status 200 (OK) means success.

function onStateChange() {
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      response = xhr.responseText;
    }
  }
}

This onreadystatechange is actually cool stuff. It helps to understand how ajax actually works, and how jQuery “is talking” to browser in order to partially load data. The onreadystatechange callback function opens even new opportunities. What if you want to show a spinner when the data has began to load? Very simple. Let’s just add a couple of code lines to our onStateChange function. When the state is 1 (loading) – show, when 4 (complete) – hide. Suppose we have a spinner object already:

function onStateChange() {
  if (xhr.readyState == 1) {
    spinner.spin();
  }
  if (xhr.readyState == 4) {
    spinner.stop();
    if (xhr.status == 200) {
      response = xhr.responseText;
    }
  }
}

By the way, if you must parse XML from response, check out this blog post by Santosh

Sys.Web.Request (Update 2013-03-05)

In ASP.NET there is a wrapper for this: Sys.Web.Request. I found it in a question on SharePoint stackexchange. Here is a simple example without any error handling, without any parameters, just to show how to invoke the Sys.Web.Request:

function onCompleted(response, eventArgs) {
    var results = response.get_responseData().d.results;
}
var url = "the actual url";
var request = new Sys.Net.WebRequest();
request.set_httpVerb("GET");
request.set_url(url);
request.get_headers()["Accept"] = "application/json";
request.add_completed(onCompleted);
request.invoke();

SP.RequestExecutor

Another way to make an ajax call is to use SP.RequestExecutor which is javascript utility available in SharePoint 15 (maybe not only 15?) under /_layouts/15/sp.requestexecutor.js. I found this thanks to a question on SharePoint StackExchange: Upload a non-text as an attachment using SharePoint 2013 REST API. I mentioned it in my other post: REST API: Add a plain text file as an attachment to a list item:

var request = new SP.RequestExecutor("/");
request.executeAsync({
	url: "MY URL",
	method: "GET"
});
Вула Чăвашла

VulaCV - Чăвашла вулаттаракан сайт

Discovering SharePoint

And going crazy doing it

Bram de Jager talking Office 365, SharePoint and Azure

My view and thoughts on Productivity and more.

My programming life

and everything in between

SharePoint Development Lab by @avishnyakov

It is a good place to share some SharePoint stories and development practices.

SharePoint Dragons

Nikander & Margriet on SharePoint

Paul J. Swider - RealActivity

RealActivity is a specialized healthcare services and solution advisory firm.

Mai Omar Desouki - Avid SharePointer

Egyptian & Vodafoner - Senior SharePoint Consultant

Cameron Dwyer | Office 365, SharePoint, Outlook, OnePlace Solutions

Office 365, SharePoint, OnePlace Solutions & Life's Other Little Wonders

paul.tavares

Me and My doings!

Share SharePoint Points!!

By Mohit Vashishtha

Jimmy Janlén "Den Scrummande Konsulten"

Erfarenheter, synpunkter och raljerande om Scrum från Jimmy Janlén

Aryan Nava

BigData and Blockchain expert in Toronto

SPJoel

SharePoint for everyone

SharePointRyan

Ryan Dennis is a SharePoint Solution Architect with a passion for SharePoint and PowerShell

SharePoint 2020

The Vision for a Future of Clarity

Aharoni in Unicode

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint