none
Get Navigation Taxonomy term tree in SharePoint App

    Question

  • 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?

    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.
    • Edited by Mereel Friday, August 01, 2014 3:04 AM Working code below
    Wednesday, February 26, 2014 2:08 PM

Answers

  • The method get_terms() returns a single level of terms. Try getAllTerms().

    http://msdn.microsoft.com/en-us/library/office/jj994728(v=office.15).aspx 


    certifications MCITP, MCTS, MCPD | blog http://corypeters.net | twitter @cory_peters

    • Marked as answer by Mereel Wednesday, April 23, 2014 2:01 PM
    Saturday, April 19, 2014 6:59 PM
  • This is my working code which merges the individual terms information with the fully populated objects retrieved from a GetAllTerms call.
    function loadFromSpecifiedUrl() {
        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)');
    
            var allTerms = navTermSet.getAllTerms();
            context.load(allTerms, 'Include(Id, Title, TargetUrl, FriendlyUrlSegment, Terms)');
    
            context.executeQueryAsync(function (sender, args) {
    
                var termsTree = recursor(context, allTerms, terms, [], null, termsLoaded);
                /* termsTree now contains your hierarchical navigation terms */
    
            }, function (sender, args) {
    
            });
    
        }, function () {
    
        });
    }
    
    function findLoadedTerm(allTerms, termId) {
        var termsEnumerator = allTerms.getEnumerator();
        while (termsEnumerator.moveNext()) {
            if (termsEnumerator.get_current().get_id().toString() == termId)
                return termsEnumerator.get_current();
        }
        return null; // The object was not found
    }
    
    function recursor(context, allTerms, currentNodeTerms) {
        var termsEnumerator = currentNodeTerms.getEnumerator();
        var newNodes = new Array();
    
        while (termsEnumerator.moveNext()) {
            //for the current term stub, get all the properties from the fully loaded getAllTerms object
            var currentTerm = findLoadedTerm(allTerms, termsEnumerator.get_current().get_id().toString());
            var newTerm = {
                "id": currentTerm.get_id().toString(),
                "name": currentTerm.get_title().get_value(),
                "href": currentTerm.get_targetUrl().get_value(),
                "childnodes": []
            }
    
            //populate childnodes (these will not have all properties loaded, hence recurse and replace with the getAllTerms result
            var subTerms = currentTerm.get_terms();
            if (subTerms.get_count() > 0) {
                newTerm.childnodes = recursor(context, allTerms, subTerms)
            }
    
            newNodes.push(newTerm);
        }
        return newNodes;
    }
    • Marked as answer by Mereel Friday, August 01, 2014 3:06 AM
    • Edited by Mereel Friday, August 01, 2014 3:08 AM Formatting was crap
    Friday, August 01, 2014 3:03 AM

All replies

  • The method get_terms() returns a single level of terms. Try getAllTerms().

    http://msdn.microsoft.com/en-us/library/office/jj994728(v=office.15).aspx 


    certifications MCITP, MCTS, MCPD | blog http://corypeters.net | twitter @cory_peters

    • Marked as answer by Mereel Wednesday, April 23, 2014 2:01 PM
    Saturday, April 19, 2014 6:59 PM
  • Thanks for the reply, I actually forgot about this thread after there were no replies for so long. That is essentially what I ended up doing. A getAllTerms() (including non-loaded children) which are stored in an array and which I then access as needed (by id) while recursing from the top node downward (using those child details).

    If anyone is interested I can grab a code snippet when I am next at work, if not, hopefully that explanation gets you going.

    Wednesday, April 23, 2014 2:01 PM
  • Yeah sorry to dig up an old thread but when I hit this same issue developing a solution this was the best resource I could find on Google/Bing.

    I did the same thing with getAllTerms. I built a map table to map parent ID's based on the GUID and then built a full hierarchical JavaScript object representing the true taxonomy. I haven't hit performance issues but we aren't expecting more than 6 levels deep with < 200 nodes total.


    certifications MCITP, MCTS, MCPD | blog http://corypeters.net | twitter @cory_peters

    Wednesday, April 23, 2014 3:08 PM
  • If you could share your method for turning a TermCollection into a hierarchal JS object it would be very much appreciated.
    Monday, July 28, 2014 6:56 PM
  • This is my working code which merges the individual terms information with the fully populated objects retrieved from a GetAllTerms call.
    function loadFromSpecifiedUrl() {
        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)');
    
            var allTerms = navTermSet.getAllTerms();
            context.load(allTerms, 'Include(Id, Title, TargetUrl, FriendlyUrlSegment, Terms)');
    
            context.executeQueryAsync(function (sender, args) {
    
                var termsTree = recursor(context, allTerms, terms, [], null, termsLoaded);
                /* termsTree now contains your hierarchical navigation terms */
    
            }, function (sender, args) {
    
            });
    
        }, function () {
    
        });
    }
    
    function findLoadedTerm(allTerms, termId) {
        var termsEnumerator = allTerms.getEnumerator();
        while (termsEnumerator.moveNext()) {
            if (termsEnumerator.get_current().get_id().toString() == termId)
                return termsEnumerator.get_current();
        }
        return null; // The object was not found
    }
    
    function recursor(context, allTerms, currentNodeTerms) {
        var termsEnumerator = currentNodeTerms.getEnumerator();
        var newNodes = new Array();
    
        while (termsEnumerator.moveNext()) {
            //for the current term stub, get all the properties from the fully loaded getAllTerms object
            var currentTerm = findLoadedTerm(allTerms, termsEnumerator.get_current().get_id().toString());
            var newTerm = {
                "id": currentTerm.get_id().toString(),
                "name": currentTerm.get_title().get_value(),
                "href": currentTerm.get_targetUrl().get_value(),
                "childnodes": []
            }
    
            //populate childnodes (these will not have all properties loaded, hence recurse and replace with the getAllTerms result
            var subTerms = currentTerm.get_terms();
            if (subTerms.get_count() > 0) {
                newTerm.childnodes = recursor(context, allTerms, subTerms)
            }
    
            newNodes.push(newTerm);
        }
        return newNodes;
    }
    • Marked as answer by Mereel Friday, August 01, 2014 3:06 AM
    • Edited by Mereel Friday, August 01, 2014 3:08 AM Formatting was crap
    Friday, August 01, 2014 3:03 AM