So, I’d an interesting problem. I have a SharePoint 2013 list that uses a Managed Metadata (or ‘Taxonomy’) field. I need to, through the JavaScript Client Object Model, get all items that have a particular value or a child of that value on the taxonomy tree. As an example, here’s my taxonomy: Sounds like it should be simple to query this, right? Well, not so much. The first problem that I had was get the children of the node that we wanted to query by. This didn’t seem possible to query by in CAML, so I was going to need get a set of all the ‘valid’ nodes of the taxonomy. However, getting that set of nodes is … tricky. In theory, you should be able to take the selected node, and recurse over the child nodes, getting their IDs. The Term.getTerms() function suggests this is possible – but the documentation tells you nothing about how to actually use it. I simply couldn’t get this working, and to be honest, I was concerned about potentially making LOTS of JSOM requests to traverse this tree. Instead, I realised that I could just get all of the terms (in one go!) and use their ‘path’ properties to determine if they’re a child or not. Node B is a child of A if B’s path starts with A’s path. Great, so I can determine child nodes – now the next issue. The Terms have IDs that are GUIDs. The CAML query uses integer IDs that look up onto the hidden ‘TaxonomyHiddenList’. This is what it sounds like – a hidden list that stores taxonomy terms for the Site Collection, and is used as a lookup list by taxonomy columns. Therefore, I need to resolve the integer IDs for the terms, based on their GUIDs, within this list. That’s just another query, but that’s fairly straight forward. Finally, once I’ve got all the integer IDs, I can get the list items that use IDs in that set. Phew! That’s a lot, and the early parts – regarding the taxonomy – are much more complicated than it really should be. The code I ended up with was:
var term; var terms; var termGUIDs = new Array(); var termIDs = new Array(); function execGetTerms(){ termGUIDs = new Array(); termIDs = new Array(); var context = SP.ClientContext.get_current(); //Current Taxonomy Session var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context); //Term Stores var termStores = taxSession.get_termStores(); //Name of the Term Store from which to get the Terms. var termStore = termStores.getByName('Taxonomy_xgJ9gi6IRQkd7J4FOMuhMw=='); //GUID of Term Set from which to get the Terms. var termSet = termStore.getTermSet('0243a547-bcad-49e8-a8e9-4f6ebe7b8185'); term = termSet.getTerm('cdf106e5-84af-44db-8419-dbff5a221ebc'); terms = termSet.getAllTerms(); context.load(term); context.load(terms); context.executeQueryAsync( Function.createDelegate(this, getTermsComplete), function(sender,args){ alert( 'Get Terms: ' + args.get_message());} ); } function getTermsComplete() { var html = '<ul>'; var termEnumerator = terms.getEnumerator(); var termPath = term.get_pathOfTerm(); //For each term, find out if it is a child of the term we've selected. //Use the path as 'getIsDescentantOf' method always seems to return true. //http://msdn.microsoft.com/en-us/library/office/apps/dn312621.aspx while(termEnumerator.moveNext()){ var currentTerm = termEnumerator.get_current(); var currentTermPath = currentTerm.get_pathOfTerm(); if(currentTermPath.indexOf(termPath) == 0){ html+='<li>' + currentTerm.get_name() + ' [' + currentTerm.get_pathOfTerm() + ']</li>'; termGUIDs.push(currentTerm.get_id().toString()); } } html+='</ul>'; //Output interim details of what terms are this node or subnodes var div = document.getElementById('outputDiv').innerHTML = html; execGetTermIDs(); } function execGetTermIDs(){ //We've got the Term GUIDs, but need the integer IDs. //Look them up on the hidden taxonomy list. var q = '<View><Query><Where><In><FieldRef Name='IdForTerm'/><Values>'; for (var i = 0; i < termGUIDs.length; i++) { q+='<Value Type='Text'>' + termGUIDs[i] + '</Value>'; } q+='</Values></In></Where></Query></View>'; var context = SP.ClientContext.get_current(); //Query the hidden taxonomy list! var oList = context.get_web().get_lists().getByTitle('TaxonomyHiddenList'); var camlQuery = new SP.CamlQuery(); camlQuery.set_viewXml( q ); this.collTermListItem = oList.getItems(camlQuery); context.load(collTermListItem); context.executeQueryAsync( Function.createDelegate(this, getTermIDsComplete), function(sender,args){ alert( 'Get Term IDs: ' + args.get_message());} ); } function getTermIDsComplete() { //Got the IDs, create a set of them. var listItemEnumerator = collTermListItem.getEnumerator(); while (listItemEnumerator.moveNext()) { var oListItem = listItemEnumerator.get_current(); termIDs.push(oListItem.get_id()); } execGetListItems(); } function execGetListItems(){ //Now query the list using the set of integer IDs from the hidden list. var q = '<View><Query><Where><In><FieldRef Name='Term' LookupId='true' /><Values>'; for (var i = 0; i < termIDs.length; i++) { q+='<Value Type='Lookup'>' + termIDs[i] + '</Value>'; } q+='</Values></In></Where><OrderBy><FieldRef Name='Term' /></OrderBy></Query></View>'; var context = SP.ClientContext.get_current(); var oList = context.get_web().get_lists().getByTitle('CustomListA'); var camlQuery = new SP.CamlQuery(); camlQuery.set_viewXml( q ); this.collListItem = oList.getItems(camlQuery); context.load(collListItem); context.executeQueryAsync( Function.createDelegate(this, getListItemsComplete), function(sender,args){ alert( 'Get List Items: ' + args.get_message());} ); } function getListItemsComplete() { var html = '<ul>'; var listItemEnumerator = collListItem.getEnumerator(); // Do something about displaying them. while (listItemEnumerator.moveNext()) { var oListItem = listItemEnumerator.get_current(); html += '<li>' + oListItem.get_id() + ': ' + oListItem.get_item('Title') + ' - ' + oListItem.get_item('Term').get_label() + '</li>'; } html += '</ul>'; var div = document.getElementById('outputDiv').innerHTML = html; }
And this worked! Note that the code is hard coded with Taxonomy service, term set and starting term IDs.
Hi! I have tried to do this all day! And I just found your blog! Perfect! But I have one problem with the code, I don’t understand all the “html+="</ul>";. Do you have the code with the “real” charactes so I know that to write? It’s in every method you have.
Please get back to me. I would appreciate it so much!
Looks like when I moved my blog the code got all HTML encoded by WordPress. It’s pretty easy to unencode, but I’ve updated the post for you.
Thank you! I get all the labels out on the screen now. But I can’t see any items from the list.
But I get an alert error. It can’t find the TaxonomyHiddenList. Do I need to do something? Is it called something else?
//Query the hidden taxonomy list!
var oList = context.get_web().get_lists().getByTitle(‘TaxonomyHiddenList’.
And what do you mean by IdForTerm? Should I just write in the ID?
//Look them up on the hidden taxonomy list.
var q = ”;
And by Term you mean that I sould write in the name of the term? Or the manage metadata column name? Or is it supposed to say Term?
//Now query the list using the set of integer IDs from the hidden list.
var q = ”;
and:
q+=”;
var context = SP.ClientContext.get_current();
//Look them up on the hidden taxonomy list.
var q = ”;*
The code wouldn’t post, haha. Oh, I sound so stupid right now. I figured out what you meant on the other 2 questions. But I still get the error for:
But I get an alert error. It can’t find the TaxonomyHiddenList. Do I need to do something? Is it called something else?
//Query the hidden taxonomy list!
var oList = context.get_web().get_lists().getByTitle(‘TaxonomyHiddenList’.
I figured it out! Thanks! 🙂
Hello, it seems to be pretty cool, please can you tell me how do you deploy this script?
Hard to answer that, depends what type of system you’re using. Consider the JSLink settings, or perhaps simply uploading it into the Style Library and referencing it from there.
I couldn’t get this to work. Not sure which Term Store Name and Guids to use. I realise it’s been a while, but are there some instructions you can provide?
Thanks.