Create a Managed Metadata Field Using REST and JSOM

This topic will go over creating a Managed Metadata Field using REST and JSOM. I will be using REST to create the fields, and JSOM to connect it to a term set. I’ve seen many examples on the web for connecting fields to a term set, but most of them hard-code GUIDs which isn’t helpful, since they are unique to the environment.

Lets go over the fields to create first. Even though we are technically creating one taxonomy field, we are actually creating two of them. Below is the schema xml of the fields to add.

<Field ID="{04630DE4-3EDD-4B8F-8F37-601720351CDC}" Name="MyTaxonomyField_0" StaticName="MyTaxonomyField_0" DisplayName="My Taxonomy Field Value" Type="Note" Hidden="TRUE" />
<Field ID="{DA4BFB17-E27F-43A1-A51B-60172001F484}" Name="MyTaxonomyField" StaticName="MyTaxonomyField" DisplayName="My Taxonomy Field" Type="TaxonomyFieldType" ShowField="Term1033">
        <Customization>
                <ArrayOfProperty>
                        <Property>
                                <Name>TextField</Name>
                                <Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{04630DE4-3EDD-4B8F-8F37-601720351CDC}</Value>
                        </Property>
                </ArrayOfProperty>
        </Customization>
</Field>

Note - The hidden note field MUST be created first, since the taxonomy field references it. Note - The LCID will be defaulted to 1033, referring to the “ShowField” property of the taxonomy field.

The hidden note field is required for the field to work. If the taxonomy field is not connected to the hidden note field, then you will not be able to save a value to it in the list form. Now that we have the schema xml of the fields defined, let’s add them to the SharePoint web. I will be using the gd-sprest library to add the fields. I’ll show you an example of creating a field synchronously and asynchronously. It’s not required to use the library, so feel free to add the fields however you feel comfortable doing it. This example will assume that you are adding the fields to the root web of the site collection.

Asynchronously

// Get the web asynchronously, but do not execute a request to the server
$REST.Web()
    // Get the field collection
    .Fields()
    // Add the field using the schema xml
    .createFieldAsXml('[Field Schema XML]]')
    // Execute code after the field is created
    .execute(function(field) {
             // Code to execute after the field is created
    });

Synchronously

// Get the web synchronously, but do not execute a request to the server
var field = $REST.Web()
    // Get the field collection
    .Fields()
    // Add the field using the schema xml
    .createFieldAsXml('[Field Schema XML]]')
    // Execute the request
    .executeAndWait();

Now that we have the fields created, we will need to connect it to a term set dynamically. We need to set the field’s SSPId property to the term store id, and TermSetId property to the term set id. I wanted to write code to find the term set, for a specific term group, regardless of the term store. The code below will assume that the fields were added to the root web of the site collection.

var context = null;
var field = null;
var session = null;
var termGroups = [];
var termSets = null;

// Log errors
var logError = function () {
     // Log the error
     console.error("[Dev] " + arguments[1].get_message());

     // Show an error message
     SP.UI.Notify.addNotification("Error: Failed to connect the Taxonomy Field.");
}

// Method to get the term set
var getTermSet = function () {
     // Get the taxonomy session
     context = SP.ClientContext.get_current();
     session = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);

     // Get the MMS field
     field = context.get_site().get_rootWeb().get_fields().getByInternalNameOrTitle("MyTaxonomyField");
     field = context.castTo(field, SP.Taxonomy.TaxonomyField);

     // Get the term set
     termSets = session.getTermSetsByName("[[Term Set Name]]", 1033);
     context.load(termSets);

     // Execute the request
     context.executeQueryAsync(getTermStore, logError);
};

// Method to get the term store for the target term set
var getTermStore = function () {
     // Parse the term sets
     var enumerator = termSets.getEnumerator();
     while (enumerator.moveNext()) {
          // Get the term set
          var termSet = enumerator.get_current();

          // Get the term group
          var termGroup = termSet.get_group();
          context.load(termGroup);

          // Get the term store
          var termStore = termSet.get_termStore();
          context.load(termStore);

          // Save a reference to this
          termGroups.push({ termGroup: termGroup, termSet: termSet, termStore: termStore });
     }

     // Execute the request
     context.executeQueryAsync(updateFieldSource, logError);
}

// Method to update the field source
var updateFieldSource = function () {
     // Parse the term groups
     for (var i = 0; i < termGroups.length; i++) {
          // See if the term set belongs to the specific term group we are targeting
          if (termGroups[i].termGroup.get_name() == "[[Term Group Name]]") {
               // Set the taxonomy information in the field
               field.set_sspId(termGroups[i].termStore.get_id().toString());
               field.set_termSetId(termGroups[i].termSet.get_id().toString());

               // Update the field
               field.update();

               // Execute the request
               context.executeQueryAsync(function () { promise.resolve(); }, logError);
          }
     }
}

// Ensure the taxonomy class exists, and load the term set
SP.SOD.registerSod("sp.taxonomy.js", SP.Utilities.Utility.getLayoutsPageUrl("sp.taxonomy.js"));
SP.SOD.executeFunc("sp.taxonomy.js", "SP.Taxonomy.TaxonomySession", getTermSet);

Note - The LCID is defaulted to 1033 for finding the term sets by name.