SPServices Stories #18 – Retrieve Managed Metadata Using JavaScript and SPServices
- SPServices Stories #1 – How to Start a Workflow on Multiple Items in a List
- SPServices Stories #2 – Charting List Data with HighCharts
- SPServices Stories #3 – AddWebPart Method of the WebPartPages Web Service
- SPServices Stories #4 – Using SPServices to Process Files from a Non-Microsoft Source
- SPServices Stories #5 – Gritter and Sharepoint: Integrate Nifty Notifications in Your Intranet!
- SPServices Stories #6 – Custom Quizzing System
- SPServices Stories #7 – Example Uses of SPServices, JavaScript and SharePoint
- SPServices Stories #8 – CEWP, Nintex, jQuery, SPServices and the Client API
- SPServices Stories #9: Developing with Client-side Technologies: jQuery, REST, SPServices and jsRender
- SPServices Stories #10 – jqGrid Implementation Using SPServices in SharePoint
- SPServices Stories #11 – Using SPServices and jQuery to Perform a Redirect from a SharePoint List NewForm to EditForm
- SPServices Stories #12 – SharePoint and jQuery SPServices in Education: A Case Study
- SPServices Stories #13: Durandal SP3: Developing SharePoint SPAs Made Easy
- SPServices Stories #14: Create a Slide Show in SharePoint 2010 using an Announcements List and SPServices
- SPServices Stories #15: Custom Client-side Calendar Query for Office 365 SharePoint Site Using SPServices jQuery Library
- SPServices Stories #17: Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript
- SPServices Stories #18 – Retrieve Managed Metadata Using JavaScript and SPServices
- SPServices Stories #19 – Folders in SharePoint are as necessary as evil. Make the best of it using jQuery and SPServices.
- SPServices Stories #20 – Modify User Profile Properties on SharePoint Online 2013 using SPServices
- SPServices Stories #22 : SPServices SharePoint Attachments in Internet Explorer 9
- SPServices Stories #21 – Redirect If User Clicked a Button Previously
Introduction
Stefan Bauer (@StfBauer) and I have known each other for a while now. We share a passion for developing solutions in SharePoint that both function well and look good. User Experience Matters.
The other day I was working on a project where I wanted to access some managed metadata (MM) values in SharePoint 2010 using the TaxonomyClientService.This SOAP Web Service was added in SharePoint 2010 along with the Managed Metadata Service itself. It’s a pretty crufty Web Service and doesn’t do nearly as much as one would want, but it’s there.
I know that I could build what I needed myself, but I did some Bingling first to see what was out there. I ran across precious little, but this great article entitled Retrieve Managed Metadata using JavaScript and SPServices from Stefan caught my eye. In the post, Stefan does a nice job of combining CSOM calls to get the SspId, GroupId, and TermSetId – we have to have those values to query the Term Store – and SPServices to access the actual data in the Term Store. He’s also built a nice TaxConfiguration helper class to store the parsed out data. The fact that we can’t get at the GUIDs for those three Ids which specify which Services Application we need easily is one of the drawbacks of the TaxonomyClientService itself.
The project I’m working on has me trying to prepopulate MM columns in a list form. Essentially, it’s an SPCascadeDropdowns-like approach for the MM column type. In this particular case, because MM columns can’t be parsed reliably in workflows, we are storing three separate MM columns in each list item that are all related. The relationships are maintained using synonyms in the Term Sets. Entering the first one implies the values of the other two, with some potential disambiguation required on behalf of the user. The MM column functionality in the form is amazingly difficult to work with because there are multiple asynchronous calls to the Term Store using methods like /_vti_bin/TaxonomyInternalService.json/GetSuggestions and /_vti_bin/TaxonomyInternalService.json/GetMatches. Those methods aren’t well documented and the MSDN documentation tells me this:
The members of this namespace or class are reserved for internal use and are not intended to be used directly from your code.
In this case, I may resort to using them, but for now I’m focusing on the TaxonomyClientService, which is allowable. But that’s probably a post for another day.
Here are a few more links about working with taxonomy (MM) fields with JavaScript/jQuery that I’m finding helpful:
- How do I access Managed Metadata Fields with jQuery? by Patrick Lamber (@patricklamber)
- JavaScript Auto-Populate Complex Fields by Thomas Zepeda McMillan
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 Marc 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’.
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:
- GetChildTermsInTermSet
Returns all first level terms in a specified term set - GetChildTermsInTerm
provides a way to access the levels below a specific term
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.
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 !!!
Hi,
Thanks for this useful topic,
Is it possible to make mms security trimmed, I mean getting terms which are used for navigation to be security trimmed?
Kourosh:
I’m not aware of a way to do this out of the box, but I expect you could use custom properties and a custom control to make it happen in SharePoint 2013.
M.
Hi Marc,
I am trying to access Office 365/SP Online metadata using your code (SPservices).I am getting error http://… /_vti_bin/TaxonomyClientService.asmx 500 internal error
Please help.
Hi Marc,
Can we get custom/shared properties of a term using spservices. I am getting the term xml but no such property related value inside returned XML.
Thanks
Nice post!
I have a issue getting listItems that contains a field with MM, I’ve proved many ways (internal name etc), this is in SP 2013, if you have an idea please let me know!
Thanks in advance!
Michel:
What’s the issue? Are you using the code here? If you’re having trouble with SPServices, consider posting details and your code in the SPServices discussions.
M.
Great Post!
I’m trying to get a specific custom property for a term.
Is it possible with SPServices?
Thanks!