I am still struggling with getting a chain of deferred promises working on a Visual Web Part. The end goal is to change the colour of some DIV tags to colours referenced in a SPList.
I originally had this code working with a single request to lookup data from a list, but I am now looking for a chain of requests. The first is finding name of the Sub Site from the window.location.href property. Although this code is retrieving the sub site name thinking about this as if type this might be my first mistake and perhaps I should be getting this from the SPWeb object instead. The second then uses this value in a where clause to retrieve the correct data from the second call to a different list.
So the code if have at the moment works, but is intermittent. I think I'm not calling .resolve() in the correct place, and it is just luck as to if the async code has completed before the calling thread uses the expected result. It is therefore in the lap of the processor gods and probably works 50:50. This at least proves my jQuery code is producing the desired result.
I have two functions doing almost exactly the same thing to two different DIV elements. This is what I have at the moment.
function alterMenuColour(id) {
getMenuItemfromURLValue(window.location.href).done(function (urlSelection) {
var colkey = 0; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
var promise = retrieveListItems('/sites/OMGIntranet/OMGCentral/', 'MenuItemList', '<View><Query><Where><Eq><FieldRef Name=\'MenuItem\'/><Value Type=\'Text\'>' + urlSelection + '</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>', 'Id,MenuColour,BarColour', colkey);
var collMenuListItem = collListItem[colkey];
promise.done(
function (collMenuListItem) {
var listItemEnumerator = collMenuListItem.getEnumerator();
var oListItem;
while (listItemEnumerator.moveNext()) {
oListItem = listItemEnumerator.get_current();
}
var menus = getChildElementsByType(document, id, 'div');
jQuery(menus).children("div").each(function () {
jQuery(this).css("background", oListItem.get_item('MenuColour'));
});
},
function (sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
});
}
function alterBarColour() {
getMenuItemfromURLValue(window.location.href).done(function (urlSelection) {
var colkey = 1; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
var barpromise = retrieveListItems('/sites/OMGIntranet/OMGCentral/', 'MenuItemList', '<View><Query><Where><Eq><FieldRef Name=\'MenuItem\'/><Value Type=\'Text\'>' + urlSelection + '</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>', 'Id,MenuColour,BarColour', colkey);
var collBarListItem = collListItem[colkey];
barpromise.done(
function (collBarListItem) {
var listItemEnumerator = collBarListItem.getEnumerator();
var oListItem;
while (listItemEnumerator.moveNext()) {
oListItem = listItemEnumerator.get_current();
}
var bar = document.getElementsByClassName('SectionMenuBar');
jQuery(bar).each(function () {
jQuery(this).css("background", oListItem.get_item('BarColour'));
});
},
function (sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
});
}
I have created a function getMenuItemfromURLValue(url) which is the method if described earlier to get the Sub Site name.
function getMenuItemfromURLValue(url) {
var colkey = 2; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
var promise = retrieveListItems('/sites/OMGIntranet/OMGCentral/', 'SectionMenuAssignmentList', '<View><Query><OrderBy><FieldRef Name=\'ID\'/></OrderBy></Query></View>', 'MenuItemLookup', colkey);
var collSelectionMenuAssignemntListItem = collListItem[colkey];
return promise.then(
function (collSelectionMenuAssignemntListItem) {
var listItemEnumerator = collSelectionMenuAssignemntListItem.getEnumerator();
var oListItem;
while (listItemEnumerator.moveNext()) {
oListItem = listItemEnumerator.get_current();
if (isStringMatch(decodeURI(url), oListItem.get_item('MenuItemLookup').$2d_1)) {
return oListItem.get_item('MenuItemLookup').$2d_1;
}
}
},
function (sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
}
I am trying to write reusable code but it strikes me that it makes it harder to comprehend when using the deferred methods like .then()
I have a function to get the list data from SharePoint SPList which again on the same theme is reusable:
//http://www.shillier.com/archive/2013/03/04/using-promises-with-the-javascript-client-object-model-in-sharepoint-2013.aspx
function retrieveListItems(siteUrl, list, calm, include, collkey) {
var deferred = $.Deferred();
var clientContext
if (siteUrl == null) {
clientContext = sharePointCurrentClientContext();
} else {
clientContext = new SP.ClientContext(siteUrl);
}
var oList = clientContext.get_web().get_lists().getByTitle(list);
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(calm);
if (typeof this.collListItem === 'undefined') {
this.collListItem = [];
}
this.collListItem.add(collkey, oList.getItems(camlQuery));
if (include == null) {
clientContext.load(collListItem[collkey]);
} else {
clientContext.load(collListItem[collkey], 'Include(' + include + ')');
}
clientContext.executeQueryAsync(Function.createDelegate(this, function () { deferred.resolve(collListItem[collkey]) }), Function.createDelegate(this, function (sender, args) { deferred.reject(sender, args); }));
return deferred.promise();
}
Originally when I wrote this function the only way if could figure out how to return data successfully was in a global list collection object called collListItem. This is horrible I hate this and have been trying to remove it but so far unsuccessfully. It does work though, and having read and read and read, some people suggest that objects cannot be returned via deferred methods and that they have to be chained to pass on data. This again brings me back to being able to have reusable code.