[SP2013] Provider hosted app: loading scripts in order and waiting

I have finally come around to messing around with apps in SharePoint 2013 again. Way too late, I know, but better now than never right? So when I was building this provider hosted app, I wanted to make use of the JSOM libraries. When you check out some of the solutions, you will find code snippets like this:

function getHostWebUrl() {
    return decodeURIComponent(getQueryStringParameter("SPHostUrl"));
}

function getQueryStringParameter(paramToRetrieve) {
    var params = document.URL.split("?")[1].split("&");
    var strParams = "";

    for (var i = 0; i < params.length; i = i + 1) {
        var singleParam = params[i].split("=");
        if (singleParam[0] == paramToRetrieve)
            return singleParam[1];
    }
}

// Get the URI decoded app web URL.
var hostweburl = getHostWebUrl();

// The SharePoint js files URL are in the form:
// web_url/_layouts/15/resource.js
var scriptbase = hostweburl + "/_layouts/15/";

$.getScript(scriptbase + "SP.UI.Controls.js", this.renderChrome);

Okay, cool. So we’re using jQuery’s getScript method to dynamically load scripts from the SharePoint _layouts folder. But I needed much more scripts then just SP.UI.Controls. So I started adding lines like this:

 
$.getScript(scriptbase + 'init.js');
$.getScript(scriptbase + 'sp.runtime.js');

This seemed to be working at first, but soon started causing errors upon load time. These errors were in the “object undefined” category, so I figured out the sequence in which the scripts were loaded must have been wrong. That is because the wonderfull world of asynchronicity in which we cannot rely that the above calls will fire or finish in the same order you typed them. Okay, so how to fix that one? Luckily jQuery’s  getScript call also featues a callback which you can use to fire a method as soon as the getScript call is done. And that method would be the next getScript call, etc. etc. Example:

$.getScript(scriptbase + 'init.js', function () {
    $.getScript(scriptbase + 'sp.runtime.js', function () {
        $.getScript(scriptbase + 'sp.js', function () {
            $.getScript(scriptbase + 'sp.core.js');
        });
    });
});

Disclaimer: no, this is not the prettiest piece of Javascript I have ever written. There appear to be cleaner ways to acheive the same (search for promises and deferred), but the above is to illustrate the idea. Still, I was not out of the woods yet. Now the script libraries were loading in the correct order, but the code that was using them was firing too soon, before the last script was loaded. There’s that same async issue again. Final step: I first tried using $(‘document’).ready to wait for the ready method to fire. But the ready method fires when the DOM is loaded, it doesn’t care about any getScript calls that might still be busy. Fortunately, jQuery has a solution for that too: you actually can hold off the ready call untill your processing is done. For that, use $.holdReady(true); and $.holdReady(false); Example:

$.holdReady(true);

$.getScript(scriptbase + 'init.js', function () {
    $.getScript(scriptbase + 'sp.runtime.js', function () {
        $.getScript(scriptbase + 'sp.js', function () {
            $.getScript(scriptbase + 'sp.core.js', function () {
                $.getScript(scriptbase + 'init.js', funtion() {
                    $.holdReady(false);
                });
            });
        });
    });
});

The good news: this works and is reliable. The bad news: I don’t like the solution.

So I’m still going to look for a better solution. Maybe I need some scaffolding in which I can register calls which need to be fired after the scripts are loaded. Using the jquery ready method for this seems a bit off to me. So if you’re a Javascript ninja and know  a much better way: use the comments please!

Update! I have found a better solution by using RequireJS, check out this blog post: http://blog.repsaj.nl/index.php/2014/07/sp2013-loading-scripts-using-requirejs/

, , ,

Related posts

Latest posts

Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *