Share via



April 2013

Volume 28 Number 04

Microsoft Office - Exploring the JavaScript API for Office: Data Binding and Custom XML Parts

By Stephen Oliver | April 2013

This article is Part 3 in a series of in-depth walkthroughs of the JavaScript API for Office. This article continues the examination of key aspects of the API, focusing on data binding and support for working with custom XML parts. Part 1, “Exploring the New JavaScript API for Office” (msdn.microsoft.com/magazine/jj891051), provides a broad overview of the object model. Part 2, “Exploring the JavaScript API for Office: Data Access and Events” (msdn.microsoft.com/magazine/jj991976), looks closely at the important concept of how to get file content and conducts a thorough review of the Event model. Following this article, Part 4 will focus solely on the third type of app for Office: mail apps.

Throughout this series, we often make reference to the JavaScript API for Office reference documentation. You can find the official documentation, code samples and community resources at the Apps for Office and SharePoint Developer Preview page on MSDN (dev.office.com).

Data Binding in an App for Office

Data binding provides close integration between a specific region of data in the document and the app. The data in the region is bound to a named object in the app so that the app can access the data in the named region, even if the user has selected something else.

Once created, the binding persists even if the region is moved on the page (in Word) or copied to another worksheet (in Excel). For example, a binding to a table persists even if it’s renamed by the user.

When the data in the region is changed, the binding raises an event into which the app can hook. From this event, the app can get to the data that has changed and react appropriately.

Bindings and the “View” of an App Certainly, the data binding in an app for Office gives an app direct access to a set of data within the Office file, thus making it easier for the app to analyze that data without relying on direct action from the user. Yet data binding does more than just allow for targeted data access—it allows the developer to include the Office file itself as a customizable and integral component of the app.

Many apps for Office present their users with an interface contained solely within the confines of the task pane or content app UI—and there’s nothing wrong with that. Yet, in a very simple sense, the data and its presentation within the Office file is itself a “view” of the app. Users interact with their data within the Office file. They enter new data, change existing data and delete unnecessary data within the content of the document. Office applications present a view of the data that users know and understand.

The data-binding capabilities in the JavaScript API for Office allow you to leverage the view of the data that the Office application provides in an app. You, the developer, can develop an “interface” for your app using what’s already provided to you in Office. In this way, you can style the view of your app using the out-of-the-box features of the Office application. The data binding then provides the sinews that connect the view of the app to the business logic “model” contained in the JavaScript files.

Of course, the reverse is true, as well. You can use the Office file as your data source, storing the content of the data model. You can then use the app to provide a view of the data. With the flexibility of bindings, you can apply the Model-View-Controller (MVC) pattern to an app and Office file as fits your needs.

Scenarios for Using Bindings Without setting a hard limit on the creativity of developers, an app can use bindings in any combination of three generalized ways:

  • The app reacts when the user changes the data in the region.
  • The app picks up the data in the region, analyzes it, and presents the user with options for modeling or submitting the data.
  • The app pushes data from an external data source into the bound region.

Take, for example, a simple stock ticker app inserted into an Excel workbook, where one column in the workbook contains stock symbols and another contains current stock values. With data binding, an app could bind to the column with the stock symbols, picking up the stock symbols in the column. The app could then subscribe to changes in the price of those stocks via a Web service and parse the results sent from the service. Finally, the app could bind to the price column in the worksheet and update the values in real time.

We’ll do just that—create a stock ticker workbook—in the next section as we examine the Binding object.

Using the Binding Object The underlying magic of data binding is contained in the Bindings collection and the Binding object.

  • The Bindings collection represents all of the bindings created between the Office file and the app for Office. An app doesn’t have access to any bindings created by other apps.
  • The Binding object represents one named binding between a region in the Office file and the app. It exposes several members for getting, reading, and setting data, and reacting to changes in the bound region.

We’ll look more closely at these objects as we build the stock ticker app.

Before we go any further, let’s take a quick look at the data. Figure 1 shows how the view of this app looks. As you can see, we’re using fictitious stock symbols for demonstration purposes.

A Table Named “Stocks” in an Excel Workbook with Formulas and Conditional Formatting Applied
Figure 1 A Table Named “Stocks” in an Excel Workbook with Formulas and Conditional Formatting Applied

Also, we’ve already added some “intelligence” to this workbook. The region of data to which we want to bind has been formatted as a table and named “Stocks.” A custom formula has been added to the values in the right-hand column to compare the other values in the table. We also applied conditional formatting to the table to make the icon sets appear in the right-hand column.

It’s worth noting that we’ve added this workbook to our solution in Visual Studio 2012 so that we don’t have to re-create our table each time we debug the app. To add the workbook to the solution, right-click the app project in the solution (the first project listed in the Solution Explorer when using the default template), click Add Existing Item and then select your workbook. Then, in the properties for the app project, set the Start Action to your workbook file. When debugging, you’ll need to insert the app manually into your workbook (Insert tab | Apps for Office button).

When it’s initialized, the business logic of the app needs to both set the binding and then add an event handler to the event of the binding, Office.EventType.BindingDataChanged. Figure 2 shows the code. Notice that we’ve encapsulated our code within a self-executing anonymous function stored in the StockTicker variable. The name of the table on the spreadsheet, the binding name and the binding are all stored as class fields within the StockTicker “class.” The StockTicker “class” exposes only a single member: initializeBinding.

Figure 2 Creating the Binding to the Excel Workbook and Adding a Handler to the Data Changed Event in the Binding

var StockTicker = (function () {
  var tableName = "Sheet1!Stocks",
      bindingName = "Stocks",
      binding;
  // Create the binding to the table on the spreadsheet.
  function initializeBinding() {
    Office.context.document.bindings.addFromNamedItemAsync(
      tableName,
      Office.BindingType.Table,
      { id: bindingName },
      function (results) {
        binding = results.value;
        addBindingsHandler(function () { refreshData(); });
    });
  }
  // Event handler to refresh the table when the
  // data in the table changes.
  var onBindingDataChanged = function (result) {
    refreshData();
  }
  // Add the handler to the BindingDataChanged event of the binding.
  function addBindingsHandler(callback) {
    Office.select("bindings#" + bindingName).addHandlerAsync(
      Office.EventType.BindingDataChanged,
      onBindingDataChanged,
      function () {
        if (callback) { callback(); }
    });
  }
  // Other member methods of this "class" ...
  return {
    initializeBinding: initializeBinding
  };
})();

To establish a binding between the app and the table in the worksheet, we can use one of several different methods of the Document class in the JavaScript API, including addFromNamedItemAsync, addFromPromptAsync and addFromSelectionAsync. (Note that addFromPromptAsync is available only in Excel and the Excel Web App.)

Because we know the name of the region to which we want to bind—it’s the table titled “Stocks” on Sheet1—we used the addFromNamedItemAsync method to establish the binding. We passed in the name of the table using Excel range notation (Sheet1!Stocks). The results of this method call include a reference to the binding itself, allowing us to store a reference to the binding in our binding variable (class field).

In our code, we’ve passed in the Office.BindingType.Table value for the bindingType parameter of the method. This specifies that we want to create a “Table” type of binding with our data, although we also could’ve specified a text or matrix type of binding. Binding to the region as a table provides us with several benefits. For example, if the user adds a new column or row to the table, the scope of the bound region increases, too. That works the other way, as well. The TableBinding object, which underlies the binding, exposes properties for adding columns, adding rows and even deleting all of the data in the table.

(See the section titled “Accessing Office File Content from an App for Office” in the second article in this series for details about the text and matrix data types in the JavaScript API for Office.)

Our code then adds a handler to the BindingDataChanged event of the binding. When the data changes in the bound region—that is, when the user changes the data in the region—we want to call a locally defined refreshData function to start the process that updates the table. Also, because the table hasn’t yet been updated with data from the data source, we’ll want to call refreshData after the event handler has been added.

You’ll note that the addBindingsHandler function uses the Office.select method to get the binding, although we could’ve used the Bindings.getByIdAsync method instead. The major difference between the two methods is the level of access to the data returned in the results. The Office.select method returns a Binding object promise to the calling code. If the method succeeds, the Binding object returned has only a limited number of members available for use. By selecting the binding using Office.select, we can call members from the Binding object immediately. This way, we don’t have to add a callback to a function that gets the binding in order to add a handler to the binding.

(You might be thinking that we could’ve just used the local “binding” variable that captures the reference to the binding—and you’re right, we could have. We’ve written this code as it is for demonstration purposes.)

Figure 3 displays the refreshData and getBindingData functions. The refreshData function simply begins the chain of asynchronous calls that gets the table data from the worksheet by calling getBindingData. The getBindingData function contains a call to the Binding.getDataAsync method and returns the data as a TableData object.

Figure 3 Getting the Data from the Table Binding and Calling the Web Service

var StockTicker = (function () {
  // Other members of this "class"...
  // Refresh the data displayed in the bound table of the workbook.
  // This function begins a chain of asynchronous calls that
  // updates the bound table.
  function refreshData() {
    getBindingData();
  }
  // Get the stock symbol data from the bound table and
  // then call the stock quote information service.
  function getBindingData() {
    binding.getDataAsync(
      {
        startRow: 0,
        startColumn: 0,
        columnCount: 1
      },
      function (results) {
        var bindingData = results.value,
            stockSymbols = [];
        for (var i = 0; i < bindingData.rows.length; i++) {
          stockSymbols.push(bindingData.rows[i][0]);
        }
        getStockQuotes(stockSymbols);
    });
  }
  return {
    // Exposed members of the "class."
  };
})();

In the call to getDataAsync shown in Figure 3, we could’ve specified the data type to retrieve (or changed the data type) explicitly by passing in an anonymous object, {coercionType: Office.CoercionType.Table}, for the options parameter. Because we haven’t specified a data type to retrieve, the getDataAsync call returns the binding data in its original data type (a TableData object).

The TableData object, as we discussed in the second article, provides more structure to the data that we’re working with—namely, a header and a rows property that we can use to select data from the table. In this example, we just need to get the stock symbols from the first column in the table. As you might recall, the rows property stores the data in the table as an array of arrays, where each item in the first array corresponds to a row in the table.

When we work with a binding to a TableData object, we can specify a subset of the rows and columns to get from the binding, using the startRow and startColumn parameters. Both parameters specify zero-based starting points for the data to extract from the table, where the upper-left corner of the table is the point of origin. (Note that you must use the startRow and startColumn parameters together or else you’ll raise an exception.) Because we only need the first column of data from the table, we also pass in the columnCount parameter, set to 1.

Once we have that column of data, we push each value into a one-dimensional array. In Figure 3, you see that we call a getStockQuotes function that accepts the array of stock symbols as an argument. In Figure 4, we use the getStockQuotes function to retrieve data from a stock quote Web service. (For demonstration purposes, we’ve left out the code for the Web service.) After we’ve parsed the results from the Web service, we call the locally defined removeHandler method.

Figure 4 Calling the Web Service and Removing the BindingDataChanged Event Handler

var StockTicker = (function () {
  // Other members of this "class"...
  // Call a Web service to get new stock quotes.
  function getStockQuotes(stockSymbols) {
    var stockValues = [];
    // Make a call to the Web service and parse the results.
    // The results are stored in the stockValues variable, which
    // contains an array of arrays that include the stock symbol
    // with the current value.
    removeHandler(function () {
      updateTable(stockValues);
    });
  }
  // Disables the BindingDataChanged event handler
  // while the table is being updated.
  function removeHandler(callback) {
    binding.removeHandlerAsync(
      Office.EventType.BindingDataChanged,
      { handler: onBindingDataChanged },
      function (results) {
        if (results.status == Office.AsyncResultStatus.Succeeded) {
           if (callback) { callback(); }
        }
    });
  }
  return {
    // Exposed members of the "class."
  };
})();

The removeHandler function calls the binding.removeHandlerAsync method, which removes the event handler to the BindingDataChanged event. Now, if we had left that handler attached to the event, then the event would be raised when we updated the table. The event handler would then be called again and would update the table, thereby causing an infinite loop. After we’ve updated the table with the new data, we’ll add the event handler back to the event.

(Of course, we also could’ve created different bindings to separate columns in the table, using the matrix coercion type. Then we could’ve hooked up events only to the columns that users can edit.)

The removeHandlerAsync method takes a parameter, handler, which specifies the name of the handler to be removed. It’s a best practice to use the handler parameter to remove handlers from binding events.

In Figure 5, we’re going to update the table with the new stock values by calling the locally defined updateTable function.

Figure 5 Getting the Data from the Table Binding and Calling the Web Service

var StockTicker = (function () {
  // Other members of this "class"...
  // Update the TableData object referenced by the binding
  // and then update the data in the table on the worksheet.
  function updateTable(stockValues) {
    var stockData = new Office.TableData(),
        newValues = [];
    for (var i = 0; i < stockValues.length; i++) {
      var stockSymbol = stockValues[i],
          newValue = [stockSymbol[1]];
      newValues.push(newValue);
    }
    stockData.rows = newValues;
    binding.setDataAsync(
      stockData,
      {
        coercionType: Office.CoercionType.Table,
        startColumn: 3,
        startRow: 0
      },
      function (results) {
        if (results.status == Office.AsyncResultStatus.Succeeded) {
          addBindingsHandler();
        }
    });  
  }
  return {
    // Exposed members of the "class."
  };
})();

The updateTable function takes the data passed in from the Web service and then writes it back to the bound table. In this example, the stockValues parameter contains another array of arrays, where each item in the first array is an array containing a stock symbol and its current price. To set this data back into the bound table, we create a new TableData object and insert the stock value data into it.

We need to be careful that the data we set in the TableData.rows property matches the shape of the data that we’re inserting into the binding. If we blindly set a brand-new TableData object into the bound table, we run the risk of losing some of the data in our table—like the formulas, for example. In Figure 5, we added the data to the TableData object as a single column of data (an array of arrays, where each subarray contains a single item). When we insert this data back into the bound table, we need to insert this updated column of data into the appropriate column.

Here again we use the startRow and startColumn properties. The updateTable function contains a call to binding.setDataAsync that pushes the TableData back into the table in the worksheet, specifying the startColumn and startRow parameters. The startColumn parameter is set to 3, meaning that the inserted TableData object will insert its data starting at the fourth column in the table. In the callback for the setDataAsync method, we call the addBindings­Handler function again to reapply the event handler to the event.

When the binding.setDataAsync method completes successfully, the new table data is pushed into the bound region and immediately displayed. From the user’s perspective the experience is seamless. The user types data into a cell in the table, presses Enter and then the Value column of the table automatically updates.

Custom XML Parts

A particularly noteworthy feature supported by the JavaScript API for Office is the ability to create and manipulate custom XML parts in Word. In order to appreciate the deep potential of the JavaScript API for Office for custom XML parts, some background is helpful. Specifically, you need to understand how the Office Open XML (OOXML or OpenXML) file format, custom XML parts, content controls and XML mapping can be combined to create really powerful solutions—namely, solutions that involve the creation of dynamic Word documents.

OOXML Formats Office 2007 introduced the new OOXML file format for Office documents, now the default file format for Office 2010 and Office 2013. (You can tell which Office documents are in the OOXML file format because the extensions for those documents are now four-letter extensions, many of which end in “x,” for example “.docx” for a Word document, “.xlsx” for an Excel spreadsheet or “.pptx” for a PowerPoint document.)

Office documents in the OOXML format are essentially .zip files. Each .zip file contains a collection of XML files, called “parts,” which together make up the Office document. If you rename an Office document, such as a Word .docx document, to .zip, and then examine the files inside, you can see that the document is really just a collection of separate XML files, organized into folders, inside a .zip package, as shown in Figure 6.

File Structure of an Office Open XML Format Document
Figure 6 File Structure of an Office Open XML Format Document

Custom XML Parts Basics While there are standard XML parts that the Office applications always create for each new Office document in the OOXML format (for example, there’s a built-in XML part that describes core document properties), the interesting thing is that you can also add your own “custom XML” parts to a Word document, Excel workbook or PowerPoint presentation. The custom XML parts are added to the collection of XML files inside the .zip package that forms the Office document. A custom XML part is stored within the file structure of the document but isn’t displayed to the end user. This allows you to insert business data that travels with a specific instance of an Office document that’s hidden inside the file structure. You can then work with that custom XML in your app, and that’s exactly what the JavaScript API for Office supports.

Content Controls Along with the OOXML format and its file structure that allows for the inclusion of custom XML into a document, Word 2007 added content controls, a feature that richly complements custom XML parts.

Content controls are a way to define fixed regions in a Word document that hold certain kinds of data, such as plain text, rich text, pictures, dates and even repeating data. The key aspect of content controls that complements custom XML parts is data binding using XML mapping.

XML Mapping A content control can be bound or “mapped” to an element in the XML in an XML part that’s contained in the document. For example, a business could inject business data from a back-end system as a custom XML part into a Word document that has content controls mapped to the custom XML part. The content controls are bound to specific nodes in the custom XML part so when the end user opens the document, the XML-mapped content controls are automatically populated with data from the custom XML part. Or, reversing the scenario, a business could use the same Word document with mapped content controls but have the end user enter data into the content controls. When the document is saved, the data in the mapped content controls is saved back to the XML file. An application could then scrape the data from the custom XML part in the saved document and push it into a back-end system. The JavaScript API for Office provides rich support for developing applications exactly like those just described. 

Using the JavaScript API for Office to Work with Custom XML Parts The best way to walk through some of the more significant parts of the custom XML parts API in the apps for Office JavaScript Object Model is through an example. In this section, we use the “invoice manager” sample (bit.ly/YRdlwt) from the Samples area of the apps for Office and SharePoint developer portal so that you can follow along. The invoice manager sample is an example of a dynamic document scenario where a business wants to generate documents that draw data from a back-end system to produce invoices. In this case, the data is a customer’s name and shipping address, and an associated list of the customer’s purchases.  

The sample includes a template document used to create new invoices. The template document has a layout with the customer name, address and a table of customer purchases. The customer name, address and purchases sections of the document are each content controls. Each content control is mapped to a node in the schema that was created to hold customer invoice data, as shown in Figure 7.

Content Controls on the Document Surface Mapped to a Custom XML Part
Figure 7 Content Controls on the Document Surface Mapped to a Custom XML Part

The UI for the invoice manager sample app is straightforward, as shown in Figure 8.

The UI for the Invoice Manager Sample App
Figure 8 The UI for the Invoice Manager Sample App

The end user chooses an invoice number from the dropdown box in the app UI and the customer data associated with the invoice number is shown in the body of the app, as shown in Figure 9.

The Invoice Manager UI Populated with Data from a Custom XML Part
Figure 9 The Invoice Manager UI Populated with Data from a Custom XML Part

When the user chooses the Populate button, the app pushes the displayed data as a custom XML part into the document. Because the content controls are mapped to nodes in the custom XML part, as soon as the custom XML part is pushed into the document the content controls immediately show the data for each XML node to which they’re mapped. You can replace the custom XML part on the fly (as we did here), but as long as the part conforms to the schema to which the content controls are mapped, the content controls will show the mapped data. Figure 10 shows the invoice manager Packing Slip form when the content controls are mapped to a custom XML part in the document.

Content Controls Mapped to Nodes in a Custom XML Part Showing Bound Data
Figure 10 Content Controls Mapped to Nodes in a Custom XML Part Showing Bound Data

The CustomXmlParts Object

CustomXmlParts.addAsync The first step in working with custom XML parts is learning how to add them to a document using the JavaScript API for Office. The only way to do this is by using the customXml­Parts.addAsync method. As its name suggests, the customXmlParts.addAsync method adds a custom XML part asynchro­nously and has the following signature:

Office.context.document.customXmlParts.addAsync(xml [, options], callback);

Note that the required first parameter for the function is a string of XML. This is the XML for the custom XML part. As we mentioned earlier, the invoice manager uses custom XML parts that are mapped to the content controls on the document surface, but first it has to get customer data to insert as custom XML. In the InvoiceManager.js file, which holds the logic for the entire app, the app simulates getting customer data from a back-end system using the user-defined function setupMyOrders. This function creates an array of three objects that represent three customer orders. You can, of course, imagine any number of ways a business might store and get a customer’s purchase history—for example, a SQL database—but for simplicity’s sake, the app creates three “hardwired” customer orders right within the app.

Once the orders objects have been created, the data they represent must be rendered in XML so that they can be used in the call to custom­XmlParts.addAsync. That’s what happens in the initializeOrders function, which also sets up the app UI and wires up event handlers to the controls on the UI. The important piece to note is in the jQuery code that wires up an event handler for the Populate button click event, as shown in Figure 11.

Figure 11 Wiring Up an Event Handler for the Populate Button Click Event

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
    }
  });
  var xml = $.json2xml(findOrder(myOrders, selectedOrderID));
  _document.customXmlParts.addAsync(xml, function (result) { });
});

Essentially, the anonymous function that acts as the event handler for the Populate button click event converts an order object (which was created with the setupMyOrders function) into a string of XML and then calls the customXmlParts.addAsync method and passes the string of XML that contains the order info as the required first parameter.

The other parameter for customXml­Parts.addAsync is a callback. Of course, this can be a reference to a method defined elsewhere in your code, or it can be an anonymous function. The invoice manager sample uses an inline anonymous function:

_document.customXmlParts.addAsync(xml,
  function (result) { });

As is the case for all callbacks in the Java­Script API for Office, an AsyncResult object is passed in as the only argument for the callback. For customXmlParts.addAsync and all the customXmlParts functions, you can use the AsyncResult object to:

  • get a reference to the newly created custom XML part using the Async­Result.value property
  • get the result of the request using the AsyncResult.status property
  • get information about an error (if one occurred) using the Async­Result.error property
  • get your own state data (if you included any in the call to custom­XmlParts.addAsync) using the AsyncResult.asyncContext property

For that last item, remember that the other parameter in the customXmlParts.addAsync method was an optional options object:

Office.context.document.customXmlParts.addAsync(
  xml [, options], callback);

The options object is provided as a way for you to pass your own user-defined object into the call for your callback.

As you can see in the invoice manager sample, the anonymous function in the call to customXmlParts.addAsync does nothing, but in a production environment you’d probably want to do error checking to handle an instance gracefully if for some reason the custom XML part isn’t successfully added.

CustomXmlParts.getByNamespaceAsync Another key part of the JavaScript API for Office for working with custom XML parts that’s demonstrated in the invoice manager sample is the use of the customXmlParts.getByNamespaceAsync method, which you can see in the click event handler code for the Populate button. The signature for customXmlParts.getByNamespaceAsync is:

Office.context.document.customXmlParts.getByNamespaceAsync(
  ns [, options], callback);

The required first parameter, ns, is a string that specifies the namespace of the custom XML parts that you want to get. So customXmlParts.getByNamespaceAsync returns an array of custom XML parts in the document that have the namespace you specified. Because the custom XML parts created in the invoice manager sample don’t use namespaces, the call to customXmlParts.getByNamespaceAsync passes an empty string as the argument for the namespace parameter, as shown in Figure 12.

Figure 12 Using the Method CustomXmlParts.getByNamespaceAsync

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
                    }
     });
     var xml = $.json2xml(findOrder(myOrders, selectedOrderID));
     _document.customXmlParts.addAsync(xml, function (result) { });
   });
   var selOrder = $("#orders option:selected");
   popOrder(selOrder.val());

Like all asynchronous functions in the API, customXmlParts.getByNamespaceAsync has optional options and callback parameters.

CustomXmlParts.getByIdAsync The last programmatic way to get a custom XML part in a document is the customXmlParts.getByIdAsync. The signature is:

Office.context.document.customXmlParts.getByIdAsync(id [, options], callback);

This function gets a single custom XML part using the GUID of the part. You find the GUID for the custom XML part in the itemPropsn.xml file inside the document package. You can also get the GUID using the id property of the customXmlPart. A key thing to note here is that the string for the GUID must contain the curly braces (“{}”) around the GUID.

The invoice manager sample doesn’t use the customXml­Parts.getByIdAsync function, but the following code demonstrates it clearly enough:

function showXMLPartBuiltId() {
  Office.context.document.customXmlParts.getByIdAsync(
    "{3BC85265-09D6-4205-B665-8EB239A8B9A1}", function (result) {
    var xmlPart = result.value;
    write(xmlPart.id);
  });
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

In addition to the id parameter, like customXmlParts.addAsync and customXmlParts.getByNamespaceAsync, the customXml­Parts.getByIdAsync method also has the optional parameter, options, and the required parameter callback, and they’re used just as in the other functions.

The CustomXmlPart Object The customXmlPart object represents a single custom XML part. Once you get a reference to a customXmlPart using the methods from the customXmlParts object, you have several properties available, as shown in Figure 13.

Figure 13 CustomXmlPart Properties

Name Description
builtIn Gets a value that indicates whether the customXmlPart is built in.
id Gets the GUID of the customXmlPart.
namespaceManager Gets the set of namespace prefix mappings (customXmlPrefixMappings) used against the current customXmlPart.

CustomXmlPart also has events associated with it, which are shown in Figure 14.

Figure 14 CustomXmlPart Events

Name Description
nodeDeleted Occurs when a node is deleted.
nodeInserted Occurs when a node is inserted.
nodeReplaced Occurs when a node is replaced.

But for the purposes of this article, we want to focus on a few key methods of the customXmlPart object that will often be used by developers. These are shown in Figure 15.

Figure 15 CustomXmlPart Methods

Name Description
addHandlerAsync Asynchronously adds an event handler for a customXmlPart object event.
deleteAsync Asynchronously deletes this custom XML part from the collection.
getNodesAsync Asynchronously gets any customXmlNodes in this custom XML part that match the specified XPath.
getXmlAsync Asynchronously gets the XML inside this custom XML part.

CustomXMLPart.addHandlerAsync The customXmlPart.add­HandlerAsync method is key to wiring up event handlers that respond to changes to the custom XML part. The signature for the customXmlPart.addHanderAsync method is as follows:

customXmlPart.addHandlerAsync(eventType, handler [, options], callback);

Note that the first required parameter is an Office.EventType enumeration, which specifies what kind of event in the apps for Office object model you want to handle. The next required param­eter is the handler for the event. The important thing here is that when the handler is invoked, the JavaScript API for Office will pass in an event arguments parameter specific to the kind of event being handled (NodeDeletedEventArgs, NodeInsertedEventArgs or NodeReplacedEventArgs). Then, as in all asynchronous functions in the API, you have, optionally, options and callback parameters.

Consider the scenario where a document is being used like a data-entry form. The user inputs data into the form and then the form is scraped for the data. The form contains a Repeating Section content control so that each time the user enters a repeated item, a new node is added to the underlying custom XML part. Every time a node is added, or inserted, the NodeInserted event is fired and you can react to the event (and all customXmlPart events) using customXmlPart.addHandlerAsync.

Figure 16 shows how you could respond to the NodeInserted event.

Figure 16 Wiring Up an Event Handler for the CustomXmlPart.NodeInserted Event

function addNodeInsertedEvent() {
  Office.context.document.customXmlParts.getByIdAsync(
    "{3BC85265-09D6-4205-B665-8EB239A8B9A1}", function (result) {
    var xmlPart = result.value;
    xmlPart.addHandlerAsync(Office.EventType.NodeInserted,
      function (eventArgs) {
        write("A node has been inserted.");
    });
  });
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

CustomXMLPart.deleteAsync Of course, along with knowing how to add a custom XML part, it’s important to know how to delete one. The customXmlPart.deleteAsync method provides that functionality. CustomXmlPart.deleteAsync is an asynchronous function with the following signature:

customXmlPart.deleteAsync([options ,] callback);

Going back to the invoice manager sample, you can see a demonstration of customXMLPart.deleteAsync: 

$("#populate").click(function () {
  var selectedOrderID = parseInt($("#orders option:selected").val());
  _document.customXmlParts.getByNamespaceAsync("", function (result) {
    if (result.value.length > 0) {
      for (var i = 0; i < result.value.length; i++) {
        result.value[i].deleteAsync(function () {
        });
      }
    }
});

Within the click event handler for the Populate button, the program logic checks to see if any custom XML parts with “blank” namespaces exist. If they do, it deletes each one using the custom­XmlPart.deleteAsync method.

There’s much more to working with custom XML parts, but what we’ve walked through in this article should be enough to give you a sense of the rich support the JavaScript API for Office provides for custom XML parts.

Next Up: Mail Apps

In this third article of the series, we reviewed some advanced techniques for working with data in apps for Office. We showed how to add additional intelligence to a table in Excel by using data bindings. We also explored how to leverage custom XML parts in an app for Word to facilitate automated document creation.

In the next and final article in this series, we’ll examine the JavaScript API for Office as it applies to mail apps. Mail apps represent a unique set of capabilities within the JavaScript API for Office, allowing app developers and Exchange administrators to build powerful tools for working with e-mail items.


Stephen Oliver is a programming writer in the Office Division and a Microsoft Certified Professional Developer (SharePoint 2010). He writes the developer documentation for the Excel Services and Word Automation Services, along with PowerPoint Automation Services developer documentation. He helped curate and design the Excel Mashup site at ExcelMashup.com.

Eric Schmidt is a programming writer in the Office Division. He has created several code samples for apps for Office, including the popular Persist custom settings code sample. In addition, he has written articles and created videos about other products and technologies within Office programmability.

Thanks to the following technical experts for reviewing this article: Mark Brewster (Microsoft), Shilpa Kothari (Microsoft) and Juan Balmori Labra (Microsoft)
Mark Brewster graduated with a B.S. in Mathematics and Computer Science from the University of Arizona in 2008 and has been developing software at Microsoft for four years. He rides a bicycle for fun and profit, and likes to drink beer and listen to record albums.

Shilpa Kothari (Bhavsar) is a software engineer in test at Microsoft. She has worked on several Microsoft products, including Bing Mobile, Visual Studio and Office. She is passionate about software QA and user experience and can be reached at shilpak@microsoft.com.

Juan Balmori Labra is a Program Manager who has for the last three years worked on the Microsoft Office JavaScript API. Previously he worked on the Office 2010 release, shipping Business Connectivity Services and Duet.  Before pursuing his dream to move to Redmond, Juan worked at Microsoft Mexico as the Principal Architect for the Public Sector Consulting Practice.