0

I'm getting all the subwebs from a site in my app in SharePoint with:

var getW = getWebs($q)
    .then(function (results) {
        console.log(results); // Object with results from the first execute.         
    });

function getWebs($q) {

    var deferred = $q.defer();

    //App context etc..

    web = appContextSite.get_web();
    subWebs = web.getSubwebsForCurrentUser(null);

    context.load(subWebs, 'Include(Url, Created, Title, Lists)');
    context.executeQueryAsync(Function.createDelegate(this, function () {

        enumSubWebs = subWebs.getEnumerator();
        var arraySubWebs = [];
        var list = [];

        while (enumSubWebs.moveNext()) {

            var subWeb = enumSubWebs.get_current(),
            subWebUrl = subWeb.get_url();

            var _list = subWeb.get_lists().getByTitle('Custom List'); 

            list.push({
                'listItem': _list.getItemById(1),
                'webTitle': subWeb.get_title()
            });

            context.load(list[list.length - 1].listItem);

            promises.push(list);
        }

        context.executeQueryAsync(Function.createDelegate(this, function () {
            for (var i = 0; i < list.length; i++) {
                console.log(list[i].listItem.get_fieldValue()['Title']);
            }
           // Not sure what to put here, because the promise is returned to my .then above before it enters here.
        }));
        Q.allSettled(promises).then(function () {
            deferred.resolve(list);
        });
    }));
    return deferred.promise;
};

Then I would like to get a list item by ID (in 'Custom list') from each web.

I'm not sure where I'm doing anything wrong, but the the webs (incl list array) is returned, but listItem doesn't seems to be executed. How should I use the promise to return everything after the last executeQuery?

3
  • It doesn't appear that you are defining or initializing a list variable anywhere. I see you creating a _list variable, but I don't see why you would be trying to push() to that. Commented Feb 21, 2015 at 15:56
  • 1
    Please see stackoverflow.com/questions/23803743/… Commented Feb 21, 2015 at 16:03
  • JLRishe, sorry. Added. Commented Feb 21, 2015 at 17:03

2 Answers 2

1

You are using the deferred antipattern. Every asynchronous action should get its own promise that represents the result, only then you can properly compose them. Currently you have something like an array of lists of objects, but it needs to become an array of promises to work with Q.allSettled.

Actually, you don't even need Q.allSettled, as you don't fire multiple queries at the same time but only a single one.

So lets build a helper function for the promise creation:

 function load(context, objs, args) {
     if (!Array.isArray(objs))
         context.load(objs, args);
     else
         for (var i=0; i<objs.length; i++)
             context.load(objs[i], args[i]);
     var deferred = $q.defer();
     context.executeQueryAsync(function(sender, args) {
         deferred.resolve(objs); // sender, args don't seem to be helpful
     }, function(sender, args) {
         deferred.reject(args);
     });
     return deferred.promise;
}

Now you can use it like this:

function getWebs($q) {
    //App context etc..
    var web = appContextSite.get_web();
    var subWebs = web.getSubwebsForCurrentUser(null);

    return load(context, subWebs, 'Include(Url, Created, Title, Lists)')
//  ^^^^^^
    .then(function(loadedSubWebs) {
        var enumSubWebs = loadedSubWebs.getEnumerator();
        var list = [],
            loadList = [];

        while (enumSubWebs.moveNext()) {
            var subWeb = enumSubWebs.get_current(),
                subWebUrl = subWeb.get_url();

            var obj = {
                listItem: subWeb.get_lists().getByTitle('Custom List').getItemById(1),
                webTitle: subWeb.get_title()
            };
            list.push(obj);
            loadList.push(obj.listItem);
        }
        return load(context, loadList).then(function(loadedList) {
//      ^^^^^^
            for (var i = 0; i < loadedList.length; i++) {
                console.log(loadedList[i].get_fieldValue().Title);
            }
            return list;
        });
    }, function(err) {
        // the load failed. `err` will be the `args` passed to `reject`
        console.log("list doesn't exist in this web");
        // you can still resolve the promise:
        return [];
        // or alternatively rethrow the exception:
        throw err;
    });
}
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks! How will this react if a list doesn't exist? Will it break or continue the batch and return the obj?
@RangeRover: I don't know sharepoint, best try it out how executeQueryAsync behaves. The promises alone will not break, they will just resolve with the empty list.
This never gets returned to .then in getWebs if the list doesn't exist. It works if the list exists. It runs the deferred.reject in executeQ in the load function.
@RangeRover: OK, but you expect it to return that empty list? Then use an extra condition for that case.
If the list doesn't exist, I want it to return the web but without a list or just console.log for that web that the list doesn't exist under that web. Should I do this in the load function you mean?
|
0

Here's something very similar to Bergi's answer but independently derived.

The main difference is that I have left the context.load() statements behind in the main routine and promisified just context.executeQueryAsync(). This is less rational than Bergi's load() but makes for a simpler helper and simpler main routine.

var getW = getWebs($q).then(function (results) {
    console.log(results); // log the `list` derived from the first execute.         
});

function executeQueryPromisified(context) {
    /* Ref 1 */
    var deferred = $q.defer();
    context.executeQueryAsync(
        function(sender, args) {
            deferred.resolve(args);
        },
        function(sender, args) {
            deferred.reject(args);
        }
    );
    return deferred.promise;
};

function getWebs($q) {
    var web = appContextSite.get_web(),
        subWebs = web.getSubwebsForCurrentUser(null);
    context.load(subWebs, 'Include(Url, Created, Title, Lists)');
    return executeQueryPromisified(context).then(function(args) {
        var enumSubWebs = subWebs.getEnumerator(),
            list = [], subWeb, obj;
        while (enumSubWebs.moveNext()) {
            subWeb = enumSubWebs.get_current();
            obj = {
                'listItem': subWeb.get_lists().getByTitle('Custom List').getItemById(1),
                'webTitle': subWeb.get_title()
            };
            list.push(obj);
            context.load(obj.listItem);
        }
        return executeQueryPromisified(context).then(function() {
            for (var i = 0; i < list.length; i++) {
                console.log(list[i].listItem.get_fieldValue()['Title']);
            }
            return list;
        });
    }).catch(function (args) {
        /* Ref 1 */
        console.error('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
    });
};

Ref 1: Sharepoint documentation; SP.ClientContext.executeQueryAsync method

2 Comments

Thanks! How will this react if a list doesn't exist? Will it break or continue the batch and return the obj?
That depends on the failure mode(s) of .executeQueryAsync(), which I'm not familiar with. In any case, it depends what your app wants to do under failure conditions. If you were to return []; from the .catch() clause, then that would be equivalent to a "successful" but empty list. If you were to throw []; from the .catch() clause, then that would be equivalent to a "unsuccessful" empty list. Suggest you try provoking different types of failure, see what happens then devise recovery strategy(s).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.