Since I have resigned myself to the fact that I will be unable to retrieve the navigation taxonomy information from the REST services in a SharePoint app (see here
for details) which is what I have done in non-app code. I have started down the road of using SharePoint JSOM code to retrieve the navigation tree.
Unfortunately I have run into a snag here also due to JSOM lazy loading and being unable to wait for context.executeQueryAsync calls to complete in my asynchronous functions. I was wondering whether anyone has done something similar?
My non-working code:
(function () {"use strict";
// The initialize function must be run each time a new page is loaded
Office.initialize = function (reason) {
$(document).ready(function () {
app.initialize();
var scriptbase = "/_layouts/15/";
$.getScript('/_layouts/1033/init.js', function () {
$.getScript(scriptbase + 'MicrosoftAjax.js', function () {
$.getScript(scriptbase + 'sp.runtime.js', function () {
$.getScript(scriptbase + 'sp.js', function () {
$.getScript(scriptbase + 'sp.core.js', function () {
$.getScript(scriptbase + 'sp.taxonomy.js', function () {
$.getScript(scriptbase + 'sp.publishing.js', function () {
$.getScript(scriptbase + 'sp.requestexecutor.js', function () {
getAppWeb(null);
});
});
});
});
});
});
});
}).fail(function () { $('#divOutputContainer').html('<p style="colour:red">Invalid SharePoint Url, unable to load scripts.</p>'); })
$('#load-sharepoint-context').click(getTargetSharePointContext);
});
};
var appWebURL;
var web;
function getAppWeb(functionToExecuteOnReady) {
var context = SP.ClientContext.get_current();
web = context.get_web();
context.load(web);
context.executeQueryAsync(onSuccess, onFailure);
function onSuccess() {
appWebURL = web.get_url();
}
function onFailure(sender, args) {
app.initialize();
app.showNotification("Failed to connect to SharePoint. Error: " +
args.get_message());
}
}
var context
var appContextSite;
function loadFromSpecifiedUrl() {
// define ClientContext for the specified SP site
context = new SP.ClientContext.get_current();
var factory = new SP.ProxyWebRequestExecutorFactory(appWebURL);
context.set_webRequestExecutorFactory(factory);
var appContextSite = new SP.AppContextSite(context, $('#txtSharePointUrl').val());
var hostWeb = appContextSite.get_web();
var taxonomySession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var webNavSettings = new SP.Publishing.Navigation.WebNavigationSettings(context, hostWeb);
context.load(webNavSettings.get_currentNavigation())
var currentNavigationSettings = webNavSettings.get_currentNavigation();
context.load(currentNavigationSettings);
context.executeQueryAsync(function () {
var termStoreId = currentNavigationSettings.get_termStoreId();
var termSetId = currentNavigationSettings.get_termSetId();
var termStore = taxonomySession.get_termStores().getById(termStoreId);
var termSet = termStore.getTermSet(termSetId);
var navTermSet = SP.Publishing.Navigation.NavigationTermSet.getAsResolvedByWeb(context, termSet, hostWeb, 'CurrentNavigationSwitchableProvider');
context.load(navTermSet);
var navTermSet = SP.Publishing.Navigation.NavigationTermSet.getAsResolvedByWeb(context, termSet, web, 'GlobalNavigationTaxonomyProvider');
var terms = navTermSet.get_terms();
context.load(terms, 'Include(Id, Title, TargetUrl, FriendlyUrlSegment, Terms)');
context.executeQueryAsync(function (sender, args) {
recursorCount = terms.get_count();
var newData = [];
recursor(context, terms);
$("#treeview").btechcotree({
containerid: "treeview"
, dataset: { "root": newData }
, datatype: $treedatatype.Json
, dataformat: $treedataformat.Hierarchy
, target: window.name
});
}, function (sender, args) {
});
}, function () {
});
}
function recursor(context, terms) {
var termsEnumerator = terms.getEnumerator();
var newNodes = new Array();
while (termsEnumerator.moveNext()) {
var currentTerm = termsEnumerator.get_current();
var newTerm = {
"id": currentTerm.get_id().toString(),"name": currentTerm.get_title().get_value(),"href": currentTerm.get_targetUrl().get_value(),"childnodes": []
}
var subTerms = currentTerm.get_terms();
if (subTerms.get_count() > 0) {
context.load(subTerms, 'Include(Id, Title, TargetUrl, FriendlyUrlSegment, Terms)');
context.executeQueryAsync(function (sender, args) {
newTerm.childnodes = recursor(context, subTerms);
}, function (sender, args) {
});
}
newNodes.push(newTerm);
}
return newNodes;
}
})();
Everything works until I try to retrieve any information about a child node in secondary recursor calls, which of course will not work because the include does not appear to also apply to sub objects (unless these is some alternate syntax I am unaware of),
and as soon as I need to call a secondary executeQueryAsync I am no longer procedural and the method completes before any recursive calls hit the server.