Retrieve Managed Metadata using JavaScript and SPServices

In one of my last projects I ran into an interesting question regarding the taxonomy store. Will it be possible to get all taxonomy data by using pure JavaScript?

SharePoint has a great JavaScript object model for different purposes but how deep is this integration when an access to service applications like the Managed Metadata Service is required. The simple answer on this is: Not enough. Due the strict project plan we decided to use a Silverlight web part, which is capable to access the taxonomy store. This solution worked well but I wasn’t pleased by that.

Time passed by and the question left unanswered to me. At the SharePoint Conference I meet Mark D. Anderson the founder of SPServices. He told me that this Jquery library should be capable of accessing the Taxonomy Client Web Service. This was the last missing information that I needed to access the Taxonomy Store and this is my solution.

The challenges

Basically there are three gaps to take to get information from the taxonomy store.

  • Get all required data to access Taxonomy Service
  • Access the TaxonomyClientService using JavaScript
  • Handle the XML result from the web service

These goals sound simple but when it comes to JavaScript and the Taxonomy Store a lot of creativity is needed to get this done. At the end I’m really pleased with my solution. The following explanations will use a simple Metadata Field called ‘Color’.

Taxonomy in Term Store Management

Taxonomy in Term Store Management

Get all data to access Taxonomy Service

In SharePoint 2010 Managed Metadata information will always be used in columns and the configuration to the managed metadata service is directly stored in the field configuration. By accessing those site collections columns all required information can be retrieved with a dynamic and flexible approach. The following code access a field named “Color” in my site collection and returns the xml schema of the field.

var fieldCollection;

var colorField = null;
 var texconfig = null;

 // Delay Script Execution until sp.js is fully loaded
 ExecuteOrDelayUntilScriptLoaded(getColor, "sp.js");

 function getColor() {
     // Load the client context
     var clientContext = SP.ClientContext.get_current();
     if (clientContext != undefined && clientContext != null) {
         // Load the root web.
         var webSite = clientContext.get_site().get_rootWeb();;

         // Select 'Color' Field from the root web site colins
         fieldCollection = webSite.get_fields();
         this.colorField = fieldCollection.getByInternalNameOrTitle("Color");

         // Load the field or throw error
         clientContext.load(this.fieldCollection);
         clientContext.load(this.colorField);
         clientContext.executeQueryAsync(
             Function.createDelegate(this, this.OnLoadSuccess),
             Function.createDelegate(this, this.OnLoadFailed)
             );
     }
 }

 function OnLoadSuccess(sender, args) {
     var fieldInfo = '';
     // Get Schema XML from the field
     var fieldschema = colorField.get_schemaXml();
     // Parse it with the Taxonomy Configuration Object
     texconfig = TaxConfiguration;
     texconfig.Configuration = fieldschema;
     texconfig.ParseConfiguration();
    // Do something with the field information

 }

function OnLoadFailed(sender, args) {
     alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
 }

For parsing this xml schema of the field I created a simple helper class that query for all required properties to access the taxonomy store. The XML parsing is pretty much straight forward and that’s how the code looks like:

var TaxConfiguration = {
     SspId: "",
     GroupId: "",
     TermSetId: "",
     Configuration: "",
     ParseConfiguration: function () {

         xmlDoc = $.parseXML(this.Configuration);
         xml = $(xmlDoc);

         var properties = xml.find("Property");

         for (i = 0; i < properties.length; i++) {

             propertyName = properties[i].firstChild.textContent == undefined ?
properties[i].firstChild.text : properties[i].firstChild.textContent;
             propertyValue = properties[i].lastChild.textContent == undefined ?
properties[i].lastChild.text : properties[i].lastChild.textContent;

             if (propertyName == propertyValue) {
                 propertyValue = "";
             }

             switch (propertyName) {
                 case "SspId":
                     this.SspId = propertyValue;
                     break;
                 case "GroupId":
                     this.GroupId = propertyValue;
                     break;
                 case "TermSetId":
                     this.TermSetId = propertyValue;
                     break;
             }
         }

     }
 }

Somehow the xml parsing in IE and chrome is different; therefore I need to check if ‘textContent’ or simply ‘text’ exists in my jquery object. After this gap was taken everything was ready to access the taxonomy service application.

Access the TaxonomyClientService using JavaScript

As mentioned before SPServices is powerful library to access the SharePoint Web Services using JavaScript and make use Jquery. There are ways to access asp.net web services by pure JavaScript but it’s a more comfortable way to do this.

To access all hierarchy levels of a terms stored two different web service calls needs to be made because there is no web service available that returns all the information at once. Those different methods that need to be called are:

The documentation of the result returned by the web service can be found in the MS-EMMWS: Microsoft Enterprise Managed Metadata Web Service Protocol Specification. It takes a while to figure out which property is used for what. The following xml attributes will be consumed by my script:

  • a9
    Guid of the term
  • a32
    Name of the term
  • a25
    Parent termID
  • a69
    Term has child terms

After I figured out those xml attributes I was able to build my script and here is it:

function GetTermsRecursive(sspId, termSetId) {

     var terms = new Array();
     var returnValue;

     $().SPServices({
         operation: "GetChildTermsInTermSet",
         sspId: sspId,
         termSetId: termSetId,
         lcid: 1033,
         completefunc: GetData
     });
 }

 function GetChildTermsRecursive(sspId, termSetId, roottermId) {

     var terms = new Array();

     $().SPServices({
         operation: "GetChildTermsInTerm",
         sspId: sspId,
         termId: roottermId,
         termSetId: termSetId,
         lcid: 1033,
         completefunc: GetData
     });

     return terms;

 }

 function GetData(xData, Status) {
     if (Status == "success") {

         terms = new Array();

         xmlData = xData;

         // Fix for different XML parsing in IE and Chrome
         termsContent = $.parseXML(xmlData.responseText).firstChild.textContent == undefined ?
                             $.parseXML(xmlData.responseText).text :
                             $.parseXML(xmlData.responseText).firstChild.textContent;

         termsXML = $.parseXML(termsContent);
         $termsXML = $(termsXML);
         console.log($termsXML);

         childTerms = $termsXML.find("T");
         parentTermId = null;

         filterOutput = "<ul>";

         for (i = 0; i < childTerms.length; i++) {

             termName = $(childTerms[i]).find("TL");
             hasChildTermsXml = $(childTerms[i]).find("TM");

             // request if child terms are available
             hasChildTerms = $(hasChildTermsXml).attr("a69");

             var tsTerm = new Object();

             // Requesting actual term id
             tsTerm.termId = $(childTerms[i]).attr("a9");

             // Requesting term name
             tsTerm.termName = termName.attr("a32");

             // Setting Parent Term ID
             parentTermId = $(hasChildTermsXml).attr("a25");

             filterOutput += "<li id='" + tsTerm.termId + "'>" + tsTerm.termName;

             // If child terms are avaliable query child terms
             if (hasChildTerms != undefined && hasChildTerms == "true") {

                 // Request child Terms
                 tsTerm.child = GetChildTermsRecursive(taxconfig.SspId, taxconfig.TermSetId, tsTerm.termId);
                 tsTerm.hasChildTerms = true;

             } else {

                 tsTerm.child = null;
                 tsTerm.hasChildTerms = false;

             }

             filterOutput += "</li>";
             terms[i] = tsTerm;

         }

         filterOutput += "</ul>";

         // If parent element is specified query for parent element
         if (parentTermId != undefined || parentTermId != null) {

             $("#" + parentTermId).append(filterOutput);

         } else {

             currentFilter = $("#filter").html();
             $("#filter").html(currentFilter + filterOutput);

         }
         return terms;

     }
 }

So that’s all needed to return and output the result in SharePoint. For displaying those terms I added a HTML Form web part to the page that contains a simple div element with the id called filter.

Output of javascript.from the Managed Meta Data Service

Output of JavaScript.from the Managed Meta Data Service

I decided to write a simple list to the defined div but it can be transformed to any other structure.

Conclusion

What looks like a complex problem at the beginning was easy to solve by using SPServices. In future projects now I’m able to use pure JavaScript to access Managed Metadata Service. The code for that is highly reusable too. I’m now able to access any site collection field based on managed metadata by using my TaxConfiguration object. It’s flexible and dynamic and hard coding of the required information to access the managed metadata service is not needed anymore. SPServices is also capable to boost productivity. In future if in need to decide to use Silverlight or JavaScript to access the Managed Metadata Service I will prefer to use JavaScript for a simple reason. Silverlight wasn’t made to write html to a web page. I think this is a misuse of Silverlight in this case.

If you like to download and play around yourself you can download the scripts packed up in a web part. All that needs to be done in the web part is to change the references to the SPServices and the name of the field in the getColor function.

Download JavaScript Taxonomy Web Part

Have fun with the taxonomy store !!!

This entry was written by Stefan Bauer , posted on Sunday December 11 2011at 03:12 am , filed under SharePoint 2010 and tagged , , , , . Bookmark the permalink . Post a comment below or leave a trackback: Trackback URL.

2 Responses to “Retrieve Managed Metadata using JavaScript and SPServices”

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>