Skip to content
Aymeric edited this page Aug 28, 2017 · 7 revisions

It can be a bit tricky to retrieve a Term Set. Hopefully, SharepointPlus (from v4.1) can help you.

Retrieve the Taxonomy Properties from the Managed Metadata column

After you have created your term set, and after you have added a Managed Metadata column into your list, you'll be able to retrieve the properties for the term set defined for your Managed Metadata column.

To do so, you need to get the info for your list. Below is an example with a list called My_List and a Managed Metadata column called Tags:

var taxonomyConfiguration = {sspId:"", termSetId:""};
$SP().list("My_List").info(function(fields) {
  for (var i=0; i<fields.length; i++) {
    if (fields[i]["DisplayName"] === "Tags") {
      console.log("sspId => ",fields[i].Property.SspId);
      console.log("termSetId => ",fields[i].Property.TermSetId);
      taxonomyConfiguration.sspId = fields[i].Property.SspId;
      taxonomyConfiguration.termSetId = fields[i].Property.TermSetId;
    }
  }
});

Retrieve Term Set

We have three solutions.

1. Using SOAP Web Services

Now that you have sspId (note the 'I' in upper case) and termSetId, you can get the term set properties.

While retrieving the XML from the server, you'll find some unobvious names for properties. Here some tips:

Property a9 is the termId
Property a32 is the termName
Property a25 is the parentTermId
Property a69 is true when there are children

Below is the valid code (using Promise) that will return all your terms in your set:

function getAllTerms() {
  $SP().webService({
    service:"TaxonomyClientService",
    operation:"GetChildTermsInTermSet",
    webURL:"https://my.website.com",
    soapURL:"http://schemas.microsoft.com/sharepoint/taxonomy/soap/",
    properties:{
      sspId:taxonomyConfiguration.sspId,
      lcid:1033,
      termSetId:taxonomyConfiguration.termSetId
    }
  }).then(function(data) {
    return parseTermXML(data.querySelector('GetChildTermsInTermSetResult'))
  })
}

// data is the content returned by the server
function parseTermXML(data) {
  return new Promise(function(resolve, reject) {
    var domParser, xml, t, i, terms=[];
    // parse the result to XML
    domParser = new window.DOMParser();
    xml = data.firstChild.nodeValue;
    xml = domParser.parseFromString(xml, "text/xml");
    t = xml.querySelectorAll('T');
    if (t.length === 0) resolve(terms);
    else {
      for (i=0; i<t.length; i++) {
        terms.push({
          Name:t[i].querySelector('TL').getAttribute("a32"),
          ID:t[i].getAttribute("a9"),
          Children:(t[i].querySelector('TM').getAttribute("a69") == "true" ? [] : null)
        });
      }

      // if we have children, then we find them
      var dfd = Promise.resolve();
      var res = terms.map(function(term) {
        dfd = dfd.then(function() {
          if (term.Children === null) {
            term.Children=[];
            return Promise.resolve(term);
          } else {
            return getTerm(term).then(function(children) {
              term.Children = children;
              return Promise.resolve(term);
            })
          }
        });
        return dfd
      });
      Promise.all(res).then(function(terms) {
        resolve(terms)
      })
    }
  })
}

// get sub terms
function getTerm(term) {
  return $SP().webService({
    service:"TaxonomyClientService",
    operation:"GetChildTermsInTerm",
    webURL:"https://my.website.com",
    soapURL:"http://schemas.microsoft.com/sharepoint/taxonomy/soap/",
    properties:{
      sspId:taxonomyConfiguration.sspId,
      lcid:1033,
      termSetId:taxonomyConfiguration.termSetId,
      termId:term.ID
    }
  }).then(function(data) {
    return parseTermXML(data.querySelector('GetChildTermsInTermResult'))
  })
}

getAllTerms().then(function(terms) {
  console.log("All the terms => ",terms)
})

The downside with using SOAP Web Service is we need to do one request by group of terms. So depending how big is your term store, it will become too much heavy.

2. Using JSOM

On this blog post you can have an interesting method to get terms from JSON, or from Stackoverflow too. It's faster because it permits to retrieve all the terms with 2 requests (one for context/digest and one for the terms).

3. Hacking JSOM

(my favorite one because it will use far less code and requests than from the other 2 solutions)

I don't like JSOM, especially because I don't necessary want to load all the crapy Sharepoint Javascript files. So we can simply simulate the AJAX request that is sent and parse the result.

The full code for SharepointPlus 4.1:

var siteurl="https://my.website.com";
function getAllTerms(termSetId) {
  return new Promise(function(resolve, reject) {
    // retrieve context info for FormDigestValue
    $SP().ajax({
      method:'POST',
      url:siteurl+'/_api/contextinfo',
      beforeSend: function(xhr) { xhr.setRequestHeader('Accept', 'application/json;odata=verbose'); },
      success:function(data) {
        resolve(data)
      }
    })
  }).then(function(data) {
    var requestdigest=(typeof data === "string" ? JSON.parse(data) : data).d.GetContextWebInformation.FormDigestValue;
    // retrieve terms by simulating the request sent with JSOM
    return new Promise(function(prom_resolve, prom_reject) {
      var data = '<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="15.0.0.0" ApplicationName="Javascript Library"><Actions><ObjectPath Id="149" ObjectPathId="148" /><ObjectIdentityQuery Id="150" ObjectPathId="148" /><ObjectPath Id="152" ObjectPathId="151" /><ObjectIdentityQuery Id="153" ObjectPathId="151" /><ObjectPath Id="155" ObjectPathId="154" /><ObjectIdentityQuery Id="156" ObjectPathId="154" /><ObjectPath Id="158" ObjectPathId="157" /><Query Id="159" ObjectPathId="157"><Query SelectAllProperties="true"><Properties /></Query><ChildItemQuery SelectAllProperties="true"><Properties /></ChildItemQuery></Query></Actions><ObjectPaths><StaticMethod Id="148" Name="GetTaxonomySession" TypeId="{981cbc68-9edc-4f8d-872f-71146fcbb84f}" /><Method Id="151" ParentId="148" Name="GetDefaultSiteCollectionTermStore" /><Method Id="154" ParentId="151" Name="GetTermSet"><Parameters><Parameter Type="String">'+termSetId+'</Parameter></Parameters></Method><Method Id="157" ParentId="154" Name="GetAllTerms" /></ObjectPaths></Request>';
      $SP().ajax({
        method:'POST',
        url:siteurl+'/_vti_bin/client.svc/ProcessQuery',
        beforeSend: function(xhr) { xhr.setRequestHeader('X-RequestDigest', requestdigest) },
        data:data,
        success:function(response) {
          prom_resolve(response);
        },
        error:function(err) {
          prom_reject()
        }
      })
    })
  }).then(function(response) {
    return new Promise(function(prom_resolve, prom_reject) {
      var terms=[]
      response.forEach(function(obj) {
        if (obj._ObjectType_ && obj._ObjectType_==="SP.Taxonomy.TermCollection") {
          obj._Child_Items_.forEach(function(term) {
            terms.push(term)
          })
        }
      });
      // sort based on path
      terms.sort(function(a,b) {
        if (a.PathOfTerm < b.PathOfTerm) return -1;
        if (a.PathOfTerm > b.PathOfTerm) return 1;
        return 0
      })
      prom_resolve(terms)
    })
  })
}

getAllTerms(taxonomyConfiguration.termSetId).then(function(terms) {
  console.log(terms) // 'terms' is a nice array with all the properties returned for the terms
})

And the same thing but with SharepointPlus 5.0+:

var siteurl="https://my.website.com";
function getAllTerms(termSetId) {
  // retrieve terms by simulating the request sent with JSOM
  var data = '<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="15.0.0.0" ApplicationName="Javascript Library"><Actions><ObjectPath Id="149" ObjectPathId="148" /><ObjectIdentityQuery Id="150" ObjectPathId="148" /><ObjectPath Id="152" ObjectPathId="151" /><ObjectIdentityQuery Id="153" ObjectPathId="151" /><ObjectPath Id="155" ObjectPathId="154" /><ObjectIdentityQuery Id="156" ObjectPathId="154" /><ObjectPath Id="158" ObjectPathId="157" /><Query Id="159" ObjectPathId="157"><Query SelectAllProperties="true"><Properties /></Query><ChildItemQuery SelectAllProperties="true"><Properties /></ChildItemQuery></Query></Actions><ObjectPaths><StaticMethod Id="148" Name="GetTaxonomySession" TypeId="{981cbc68-9edc-4f8d-872f-71146fcbb84f}" /><Method Id="151" ParentId="148" Name="GetDefaultSiteCollectionTermStore" /><Method Id="154" ParentId="151" Name="GetTermSet"><Parameters><Parameter Type="String">'+termSetId+'</Parameter></Parameters></Method><Method Id="157" ParentId="154" Name="GetAllTerms" /></ObjectPaths></Request>';
  return $SP().ajax({
    body:data,
    url:siteurl+'/_vti_bin/client.svc/ProcessQuery'
  }).then(function(response) {
    var terms=[];
    response.forEach(function(obj) {
      if (obj._ObjectType_ && obj._ObjectType_==="SP.Taxonomy.TermCollection") {
        obj._Child_Items_.forEach(function(term) {
          terms.push(term)
        })
      }
    });
    // sort based on path
    terms.sort(function(a,b) {
      if (a.PathOfTerm < b.PathOfTerm) return -1;
      if (a.PathOfTerm > b.PathOfTerm) return 1;
      return 0
    })
    return Promise.resolve(terms)
  })
}

getAllTerms(taxonomyConfiguration.termSetId).then(function(terms) {
  console.log(terms)
})

So we needed 2 requests only (one to get context/digest and one to get all the terms), and without loading sp.taxonomy.js or even sp.js.