Nathan Kamenar Nathan Kamenar - 3 months ago 18
AngularJS Question

Angular $q Settle all

I am learning Angular and creating a service for working with SharePoint data in the process. I have a function now that updates SharePoint items but I am having a problem. If the function is called and encounters an error while trying to update an item, all the items finish updating but only the first reject (failed item) is called. If any other items fail reject is not called for them. I would like to be able to push all errors generated into an array that I can pass back to the user so they may handle all of the errors. Below is my code:

this.UpdateListItems = function (webUrl, listName, itemsJson) {
var deferred = $q.defer();
var promises = [];

angular.forEach(itemsJson, function (itemProps) {
var itemPromise = this.UpdateListItem(webUrl, listName, itemProps.Id, itemProps)
.then(function (response) {
return deferred.resolve(response);
}, function (error) {
return deferred.reject(error);
});
promises.push(itemPromise)
}, this);

return $q.all(promises)
.then(function (data) {
return lists[listName];
}, function () {
console.info("Promises: " + JSON.stringify(promises));
return $q.reject(promises);
});
};


Also here is the JSON promises returned. You will see 4 promise objects are returned with the first 2 being pending (they succeed) then the third one fails because the item doesn't exist on the server. Finally the 4th one should also fail for the same reason as the third but as you can see the reject returns immediately when the first one fails and doesn't process any further rejected items:

[
{
"$$state": {
"status": 0,
"pending": [
[
{
"promise": {
"$$state": {
"status": 0
}
}
},
null,
null,
null
]
]
}
},
{
"$$state": {
"status": 0,
"pending": [
[
{
"promise": {
"$$state": {
"status": 0
}
}
},
null,
null,
null
]
]
}
},
{
"$$state": {
"status": 2,
"value": {
"data": {
"error": {
"code": "",
"message": {
"lang": "en-US",
"value": "Resource not found for the segment 'TestList'."
}
}
},
"status": 404,
"config": {
"method": "GET",
"transformRequest": [
null
],
"transformResponse": [
null
],
"url": "http://kl-sp10dev04/sites/angular/_vti_bin/listdata.svc/TestList(126)",
"processData": false,
"headers": {
"Accept": "application/json;odata=verbose"
}
},
"statusText": "Not Found"
},
"processScheduled": false
}
},
{
"$$state": {
"status": 0,
"pending": [
[
{
"promise": {
"$$state": {
"status": 0
}
}
},
null,
null,
null
]
]
}
}
]

Answer

For anyone who runs into this problem in the future here is the answer. This is the code I am now using and it works as intended. That is it returns a promise which waits for all item promises to finish. If all items update successfully then it resolves by returning a reference to the JSON objects list. If any of items fails to update it rejects an array of all errors back to the user for error handling. Note that the Lists array is a property of the service so you don't see it defined here.

this.UpdateListItems = function (webUrl, listName, itemsJson) {
    var promises = [];
    var errors = [];
    angular.forEach(itemsJson, function (itemProps) {
        promises.push(this.UpdateListItem(webUrl, listName, itemProps.Id, itemProps)
            .then(function (response) {
                return itemsJson[listName];
            }, function (error) {
                errors.push(error);
            }));
    }, this);
    return $q.all(promises)
        .then(function () {
            if (errors.length > 0) {
                return $q.reject(errors);
            }
            else {
                return lists[listName]
            }
        });
};