This blog will show you how to access the Taxonomy Term Store, how to iterate through all the items. How to add a Group, TermSet and Term, and lastly how to delete them. The Taxonomy Term store can be accessed in a SharePoint App, with permission scope of Taxonomy. If you wish to make changes to the Taxonomy Term Store then you will need to ensure to give Write permission. To be able to use the JSOM API for Taxonomy you need to reference SP.Taxonomy.js found in _layouts/15/ the best way to reference this would be in the document ready.
$(document).ready(function () { var scriptbase = _spPageContextInfo.webServerRelativeUrl + "/_layouts/15/"; $.getScript(scriptbase + "SP.Runtime.js", function () { $.getScript(scriptbase + "SP.js", function () { $.getScript(scriptbase + "SP.Taxonomy.js", function () { context = SP.ClientContext.get_current(); //Call your code here. getTermStores(); }); }); }); });
Reading from Taxonomy
Obtaining All the Term Stores.
Typically there is only one term store per web application, but there is always a chance there is more than one. You can get the term store by name if you know it.
function getTermStores() { session = SP.Taxonomy.TaxonomySession.getTaxonomySession(context); termStores = session.get_termStores(); context.load(session); context.load(termStores); context.executeQueryAsync( function(){ termStoresEnum = termStores.getEnumerator(); var termStores = "Term Stores: /n"; while (termStoresEnum.moveNext()) { var currentTermStore = termStoresEnum.get_current(); var termStoreID = currentTermStore.get_id(); var termStoreName = currentTermStore.get_name(); termStores += "Name: " + termStoreName + " ID:" + termStoreID; } }, function(){ //failure loading termstores. }); }
Get the default TermStore
There is also a method to .getDefaultSiteCollectionTermStore(). See my post getDefaultSiteCollectionTermStore() – JavaScript runtime error: Unable to get property ‘toString’ of undefined or null reference if you get a JavaScript error if you try to access any properties after a successful callback.
function defaultTermStore() { session = SP.Taxonomy.TaxonomySession.getTaxonomySession(context); termStore = session.getDefaultSiteCollectionTermStore(); context.load(session); context.load(termStore); context.executeQueryAsync(successDefaultTermStore, failedListTaxonomySession); }
Obtaining all the groups for a TermStore
To retrieve all the groups from within a Term Store, you need to have the Term Store ID. You can get any Group by knowing the ID using termStore.getGroup().
function showGroups(termStoreId) { var termStoresEnum = termStores.getEnumerator(); //Loop through termstores. while (termStoresEnum.moveNext()) { var currentTermStore = termStoresEnum.get_current(); //If match load the TermStore. if (currentTermStore.get_id().toString() == termStoreId.toString()) { context.load(currentTermStore); context.executeQueryAsync( function () { //Get Groups and load them. groups = currentTermStore.get_groups(); context.load(groups); context.executeQueryAsync( function () { var groupList = "Groups: \n"; var groupsEnum = groups.getEnumerator(); //Loop through each group. while (groupsEnum.moveNext()) { var currentGroup = groupsEnum.get_current(); var groupName = currentGroup.get_name(); var groupId = currentGroup.get_id(); groupList += groupName + ": " + groupId + "\n"; } alert(groupList); }, function () { //failure loading groups }); }, function () { //failure loading termStore }); break; } } }
Obtaining all the TermSets for a Group
To retrieve all the term sets from within a Group, you need to have the Group ID. You can get any TermSet by knowing the ID using termStore.getTermSet(<GUID>).
function showTermSets(groupId) { //we need to load and populate the matching group first, or the term sets that it contains will be inaccessible to our code. var groupEnum = groups.getEnumerator(); while (groupEnum.moveNext()) { var currentGroup = groupEnum.get_current(); if (currentGroup.get_id() == groupId) { context.load(currentGroup); context.executeQueryAsync( function(){ //Get Term Sets and load them. var termSets = currentGroup.get_termSets(); context.load(termSets); context.executeQueryAsync( function(){ var termSetEnum = termSets.getEnumerator(); var termSetList = "Term Sets: \n" while(termSetEnum.moveNext()){ var currentTermSet = termSetEnum.get_current(); var termSetName = currentTermSet.get_name(); var termSetId = currentTermSet.get_id(); termSetList += termSetName + ": " + termSetId + "\n"; } alert(termSetList); }, function(){ //Failure loading Term Sets }); }, function () { //Failure loading Group. }); break; } } }
Obtaining all the Terms for a group.
Terms are not as easy to grab, due to the fact that each Term can have a child term, and them terms have children themselves. Therefore in the following code we need a recursive method to ensure we loop through. You can get any Term (from any level) of a Term Set by knowing the ID using termSet.getTerm(<GUID>).
var termsList = "Terms: \n" function showTerms(termSetId) { //We need to load and populat the matching Term Set first. var termSetEnum = termSets.getEnumerator(); while (termSetEnum.moveNext()) { var currentTermSet = termSetEnum.get_current(); if (currentTermSet.get_id() == termSetId) { //If termSet Matches, then get all terms. context.load(currentTermSet); context.executeQueryAsync( function () { //Load terms var terms = currentTermSet.get_terms(); context.load(terms); context.executeQueryAsync( function () { var termsEnum = terms.getEnumerator(); while (termsEnum.moveNext()) { var currentTerm = termsEnum.get_current(); var termName = currentTerm.get_name(); var termId = currentTerm.get_id(); termsList += termName + ": " + termId; //Check if term has child terms if (currentTerm.get_termsCount() > 0) { //Term has sub terms. recursiveTerms(currentTerm, 1); } alert(termList); } }, function () { //failure to load terms. }); }, function () { //failure to load current term set }); break; } } } function recursiveTerms(currentTerm, nestedLoop) { //Loop count for formatting purpose. var loop = nestedLoop + 1; //Get Term child terms var terms = currentTerm.get_terms(); context.load(terms); context.executeQueryAsync( function () { var termsEnum = terms.getEnumerator(); while (termsEnum.moveNext()) { var newCurrentTerm = termsEnum.get_current(); var termName = newCurrentTerm.get_name(); termId = newCurrentTerm.get_id(); //Tab Out format. for (var i = 0; i < loop; i++) { termsList += "\t"; } termsList += termName + ": " + termId; //Check if term has child terms. if (currentTerm.get_termsCount() > 0) { //Term has sub terms. recursiveTerms(newCurrentTerm, loop); } } }, function () { //failure to load terms }); }
Adding to Taxonomy
Adding a Group to the Term Store.
As long as you have the Term Store ID, this is very simple piece of code.
function addGroup(termStoreId) { var groupName = "My New Group" var newGuid = new SP.Guid.newGuid(); var termStore = termStores.getById(termStoreId); var newGroup = termStore.createGroup(groupName, newGuid.toString()); context.load(newGroup); context.executeQueryAsync(function () { //success }, function () { //failed }); }
Adding a Term Set to a Group.
You will need the ID to both the Term Store and the Group to perform this function.
function addTermSet(termStoreId, groupId) { var termSetName = "New TermSet Name" var newGuid = new SP.Guid.newGuid(); var termStore = termStores.getById(termStoreId); var group = termStore.getGroup(groupId); var newTermSet = group.createTermSet(termSetName, newGuid.toString(), 1033); context.load(newTermSet); context.executeQueryAsync(function () { //success }, function () { //failed }); }
Adding a Term to a Term Set.
You will need the ID to both the Term Store and the Term Set to perform this function.
function addTerm(termStoreId, termSetId) { var termName = "My New Term" var newGuid = new SP.Guid.newGuid(); var termStore = termStores.getById(termStoreId); var termSet = termStore.getTermSet(termSetId); var newTerm = termSet.createTerm(termName, 1033, newGuid.toString()); context.load(newTerm); context.executeQueryAsync(function () { //Success }, function () { //failed }); }
Adding a Child Term to a Term.
To add a child term, you need to be aware of the Parent Term ID, as well as the Term Store ID and Term Set it belongs to.
function addSubTerm(termStoreId, termSetId, parentTermId) { var termName = "My Child Term" var newGuid = new SP.Guid.newGuid(); var termStore = termStores.getById(termStoreId); var termSet = termStore.getTermSet(termSetId); var parentTerm = termSet.getTerm(parentTermId); var newTerm = parentTerm.createTerm(termName, 1033, newGuid.toString()); context.load(newTerm); context.executeQueryAsync(function () { //Success }, function () { //failed }); }
Deleting from Taxonomy
Removing Group from Term Store.
With the ID of the Term Store and Group you can successfully delete a group from a Term Store. However, you can only delete a group if there are no term sets within it. Trying so will cause the error “The group has one or more term sets and cannot be deleted. Move or delete all term sets from the group before deleting the Group.” In my example code below I haven’t tested for this for simplicity.
function removeGroup(termStoreId, groupId) { var termStore = termStores.getById(termStoreId); var group = termStore.getGroup(groupId); group.deleteObject(); context.executeQueryAsync(function () { //success }, function () { //failed. }); }
Removing Term Set from Group.
Needing 3 IDs. Term Store, Group and Term Set you can delete Term Set from the group. If there are terms these will also be deleted, and if they were used within your site they will become orphaned. It might be a good idea to present this warning to your users before allowing the deletion. In my example below I haven’t given this warning for simplicity.
function removeTermSet(termStoreId, groupId, termSetId) { var termStore = termStores.getById(termStoreId); var termSet = termStore.getTermSet(termSetId); termSet.deleteObject(); context.executeQueryAsync(function () { //success }, function () { //failed }); }
Removing Term from Term Set or Parent Term
This method will take care of deleting a Term from any level. Mainly because when you call getTerm(<GUID>) you do so from the Term Set. It doesn’t matter where it is in the term stack, it can be found. Requiring just Term Store, Term Set and Term IDs.
function removeTerm(termStoreId, termSetId, termId) { var termStore = termStores.getById(termStoreId); var termSet = termStore.getTermSet(termSetId); var term = termSet.getTerm(termId); term.deleteObject(); context.executeQueryAsync(function () { //success }, function () { //failed. }); }
Hopefully the above will give you enough to be able to manipulate/obtain information from the Term Store through a SharePoint App, or just by using JavaScript.
Bonjour à tous..(hi everybody)
First thank you for this very interresting code,
I am new in javascript because that is the first function that i did in this language..
recursive function and assynchron call in a while loop cannot work..(maybe i am wrong)
My code works but it s not finished, i explain:
For iterate from default termstore to Termset Level i used a combination of iteration and lock flag.
/*————————Fonction lecture du Default Term Store———————————*/
function onreadDefaultTermStoreSuccess() {
//——-Infos sur le Term Store
//var termStoreID = defaultTermStore.get_id();
//var termStoreName = defaultTermStore.get_name();
var groups = defaultTermStore.get_groups();
context.load(groups);
//—–depart la requete asynchrone.
context.executeQueryAsync(function onreadTermGroupsSucces() {
//récuperer l’énumerateur des groupes.
var groupsEnum = groups.getEnumerator();
//créer un timer qui vérifie que la requete précedente est terminée.
groupsInterval = setInterval(function () {
if (GroupsbusyIndicator === false) {
GroupsbusyIndicator = true;
var hasGroups = groupsEnum.moveNext();
if (hasGroups) {
var currentGroup = groupsEnum.get_current();
context.load(currentGroup);
context.executeQueryAsync(function () {
var termSetsCollections = currentGroup.get_termSets();
context.load(termSetsCollections);
context.executeQueryAsync(function () {
var termSetsCollectionEnum = termSetsCollections.getEnumerator();
//préparer ici un second timer pour intégrer la fonction recursive
/*—————————–TIMER——————————–*/
termSetsInterval = setInterval(function () {
if (TermSetsbusyIndicator === false) {
TermSetsbusyIndicator = true;
var hasTermSet = termSetsCollectionEnum.moveNext();
if (hasTermSet) {
var currentTermSet = termSetsCollectionEnum.get_current();
var termsCollection = currentTermSet.get_terms();
context.load(termsCollection);
context.executeQueryAsync(function () {
var termsEnum = termsCollection.getEnumerator();
while (termsEnum.moveNext()) {
var currentTerm = termsEnum.get_current();
var termName = currentTerm.get_name();
var termId = currentTerm.get_id();
result += termName + “\n”;
globalTerms.push(new TermJs(currentTermSet, termName, termId));
}
TermSetsbusyIndicator = false;
}, onloadTermsCollectionFail);
} else {
clearInterval(termSetsInterval);
GroupsbusyIndicator = false;
TermSetsbusyIndicator = false;
}
}
}, 100);
/*—————————–TIMER——————————–*/
}, onloadTermSetsCollectionFail);
}, onloadCurrentGroupFail);
} else {
clearInterval(groupsInterval);
GroupsbusyIndicator = false;
$(“#resultat”).html(globalTerms);
}
}
}, 100);
}, onreadTermGroupsFail);
}
/*————————Fin Fonction lecture du Default Term Store———————————*/
and when i arrive to term level instead recursive call i use javscript object model like this!
function TermJs(currentTermSet, Name, Guid) {
this.Guid = Guid;
this.Name = Name;
this.currentTermSet = currentTermSet;
this.currentTerm = null;
this.LoadYourChilds = function LoadYourChilds() {
this.TermJsCollectionEnum = this.SPTermsCollection.getEnumerator();
while (this.TermJsCollectionEnum.moveNext()) {
this.TermsJsCollection.push(new TermJs(this.currentTermSet, this.TermJsCollectionEnum.get_current().get_name(), this.TermJsCollectionEnum.get_current().get_id()));
}
}
this.LoadYourself = function LoadYourself() {
this.TermsJsCollection = new Array();
this.SPTermsCollection = this.currentTerm.get_terms();
context.load(this.SPTermsCollection);
context.executeQueryAsync(Function.createDelegate(this, this.LoadYourChilds), Function.createDelegate(this, this.failed));
}
this.Load = function () {
this.currentTerm = this.currentTermSet.getTerm(this.Guid);
context.load(this.currentTerm);
context.executeQueryAsync(Function.createDelegate(this, this.LoadYourself), Function.createDelegate(this, this.failed));
}
this.Load();
this.failed = function failed(_parameter) {
}
I try and my general array with all term set works …but if somebody can explain to me what exactly do “context.executeQueryAsync(Function.createDelegate(this, this.LoadYourself)”
you are welcome,
Pingback: Extract the Term Sets and their custom properties using CSOM in SharePoint 2013 | The Relentless FrontEnd - Aackose Lal's Blog
Hi,
I am doing this in a loop. Please find a sample below. In this, for every iteration of the ($eles.each) loop, I want the function to wait and execute the onSucceeded callback method before proceeding to the next iteration. But, somehow, I am not able to get it done, probably because of async call. Can you please help me to achieve this?
var context = SP.ClientContext.get_current();
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var termStores = taxSession.get_termStores();
$eles.each(function() {
var $ele = $(this);
var sspID = //getting the Term Store ID here;
var termSetId = //getting the Term Set ID here;
var termStore = termStores.getById(sspID);
var termSet = termStore.getTermSet(termSetId);
var terms = termSet.getAllTerms();
context.load(terms);
context.executeQueryAsync(Function.createDelegate(this, this.onSucceeded),
Function.createDelegate(this, this.onFailed));
});
function onSucceeded(sender, args)
{
}
function onFailed(sender, args)
{
}
Hi Ven, Thank you for reading my blog.
I didn’t know the answer to your question and I had to ask my colleague for help as he is much better with JavaScript than myself. You can check out his blog here http://salvatoredifaziosharepoint.wordpress.com/
His answer was to do something similar to the following (Please note this code is untested)
var context = SP.ClientContext.get_current();
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var termStores = taxSession.get_termStores();
if(eles.count > 0){
doJob(eles, 0)
}
function doJob(array, elementID){
var ele = array[elementID]
...
context.load(terms);
context.executeQueryAsync(Function.createDelegate(this, function(){onSucceeded(array, elementID)});
}
function onSucceeded(array, elementID)
{
var id = elementID + 1;
if(id > array.count){
doJob(array, id);
}
}
I hope this helps you.
Thanks a lot for your response. But, I am not sure, whether this will work. Since it is asynchronous and within a loop, there is no guarantee that we will get the results in sequence. In fact, can you please let me know, whether the GetRecursiveTerms method works or not? I read and tried the Deferred & Promise pattern, but that works only for the loop, but not for recursion. I am completely stuck now and don’t know how to proceed. Even the CSOM JS gives only a flat structure while calling termset.getAllTerms(). Though, we can use jQuery (SP Services) to call Taxonomy Web Service and get the results directly, this is not recommended by Microsoft. Any help will be highly appreciated.
Hi Ven,
My last example to you should give you the results you were looking for. Although it was still an async call, the next item in the array was called in the async, therefore items would have been in order.
As I said before I’m no expert in javascript, and unfortunately I haven’t really touched SharePoint 2013 for a good few months now.
Sorry I cannot help you further. Perhaps when you have solved your problem you could blog about it yourself. Thats normally why I blog something, when I’ve learnt something new.
Good luck, and if you do a blog to solve your problem leave a link to it in these comments. If you are having a problem there are bound to be others struggling too. Someone at Sharepoint.stackexchange.com might be able to help you further.
Bonjour,
I try to extend this function for loading a javascript object that represent all the sharepoint taxonomy.
I had not finished today i will start to use the recursive function , for answer the question i found a way for wait that the previous async call have finished is for , i use the set inteval function that check a global variable like this:
function onreadDefaultTermStoreSuccess() {
//——-Infos sur le Term Store
//var termStoreID = defaultTermStore.get_id();
//var termStoreName = defaultTermStore.get_name();
var groups = defaultTermStore.get_groups();
context.load(groups);
//—–depart la requete asynchrone.
context.executeQueryAsync(function onreadTermGroupsSucces () {
//récuperer l’énumerateur
var groupsEnum = groups.getEnumerator();
//créer un timer qui vérifie que la requete précedente est terminée.
var myInterval=setInterval(function() {
if (busyIndicator!==true) {
busyIndicator=true;
var bool= groupsEnum.moveNext();
if (bool!==false) {
var currentGroup = groupsEnum.get_current();
context.load(currentGroup);
context.executeQueryAsync(function(){
var termSetsCollections = currentGroup.get_termSets();
context.load(termSetsCollections);
context.executeQueryAsync(function () {var termSetCollectionEnum = termSetsCollections.getEnumerator();
//préparer ici un second timer pour intégrer la fonction recursive
while(termSetCollectionEnum.moveNext()){
var currentTermSet = termSetCollectionEnum.get_current();
alert(currentTermSet.get_name());
busyIndicator=false;
//——fin de la requete asynchrone.
}
},onloadTermSetsCollectionFail);
},onloadCurrentGroupFail);
}else{clearInterval(myInterval);busyIndicator=false;}
}
}, 100);
},onreadTermGroupsFail);
}
Hi, great post! I have one more question: did you ever manage to Change a property of a termset using jsom?
I want to Change the IsOpenForTermcreation property.
Hi, You wouldn’t believe the troubles I’ve been through to find your answer. As I haven’t opened my 2013 machine for a long time, my Managed Metadata Service wasn’t work. Once I got that working, I had to remind myself of my code. Anyway the answer to your question is yes there is a method.
As you create your new termset, there is a method called set_isOpenForTermCreation();passing in true or false;
The way I found out what methods are available was to put a break point in IE after I created a termset, then added the new termset object to watch. Under prototype showed me all the methods available.
Hope this helps you.
I believe the code above works on Sharepoint 2013 only? Is there any similar way to list the terms in Sharepoint 2010? Thanks.
Hi Mark,
You are correct this is SharePoint 2013 only code. To be able to do something similar in 2010 you would need to use the taxonomy client web service.
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.taxonomy.webservices.taxonomyclientservice.aspx
I hope this helps.
Thanks. Do you think I can use solely Sharepoint Designer and SPService to retrieve the taxonomy full list? By some reason our project try to avoid use Visual Studio to compile anything and deploy to Sharepoint.
Hi Mark,
According to my colleague Salvatore di Fazio (MVP), this would be possible. While I was trying to find a few pointers on using SPService in SharePoint designer, which I couldn’t find in the few minutes I have to reply to you. I did find this link, which seems to have the basic similarities to what you are trying to achieve. http://dougieflash.wordpress.com/2013/02/14/using-spservices-autosuggest-and-managed-metadata-in-the-term-store-part-1/
Thank you very much. I have the job done!
Hi, Is it possible to retrieve terms based on their refiners (managed properties)?
Hi Kourosh,
Thank you for reading and commenting on my blog. I haven’t tried to search on a Managed Metadata field in SharePoint 2010 or 2013. I have been meaning to look at it, and write a blog on the subject. Unfortunately the only link I have found so for on it is http://msdn.microsoft.com/en-gb/library/ff625182(v=office.14).aspx under examples. Table 2 shows you how to call search via query string against a Managed metadata column. I hope this points you in the write direction.