March 2013

Volume 28 Number 03

Microsoft Office - Exploring the JavaScript API for Office: Data Access and Events

By Stephen Oliver | March 2013

This article is the second in a series of in-depth walk-throughs of the JavaScript API for Office. Part 1 (available at msdn.microsoft.com/magazine/jj891051) provides a broad overview of the object model. This article picks up where part 1 left off, with a detailed walk-through on how to access file content and a review of the event model.

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 Center on MSDN (dev.office.com).

Accessing Office File Content from an App for Office

The JavaScript API for Office provides several basic ways for access­ing data in an Office file: You can either get or set the currently selected data, or you can get the entire file. This level of data access might sound simple and, in truth, both techniques are pretty simple to use. However, there’s a wide degree of flexibility and customization within both techniques, providing you with a lot of possibilities for your apps.

In addition to access to selected data or the entire file, the JavaScript API for Office also allows you to bind to data or to manipulate custom XML parts in the document. We’ll look more closely at these techniques for working with Office content.

Getting and Setting Selected Data

As we mentioned previously, the Document object gives an app access to the data in the file. For task pane and content apps, we can get or set selected content in an Office file using the Document.getSelectedDataAsync and Document.setSelectedDataAsync methods.

The two methods can manipulate several types of data formats that we control when we call them. Both the getSelectedDataAsync and setSelectedDataAsync methods have a parameter, coercionType, which takes a constant from the Office.CoercionType enumeration. The coercionType parameter specifies the data format of the content to get or set. Depending on the value of the coercionType parameter, we can select data as plain text, a table, a matrix, HTML or even “raw” Office Open XML (OOXML). (Note that getting and setting text as HTML or OOXML are only supported in Word 2013 as of press time.)

You don’t always have to specify a coercionType when using getSelectedDataAsync and setSelectedDataAsync. The coercionType is inferred from context whenever possible. For example, if you pass a string literal into a call to setSelectedDataAsync, then the default coercionType is “text.” If you passed the same data in as an array of arrays, then the default coercionType would be “matrix.”

We’ll give some examples of how powerful these simple methods can be, primarily using the setSelectedDataAsync method. We’ll start with some code that inserts some simple text into a Word document:

// Define some data to set in the document.
var booksToRead = "Anabasis by Xenophon; \n" +
  "Socrates' Apology by Plato; \n" +
  "The Illiad by Homer.";
// Set some data to the document as simple text.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Text },
  function (result) {
    // Access the results, if necessary.
});

Figure 1 shows the result.

Results of Inserting Data as Simple Text
Figure 1 Results of Inserting Data as Simple Text

Now we’ll change the example so that we insert the text as a “matrix” coercion type. A matrix is an array of arrays that’s inserted as a simple range of cells (Excel) or simple table (Word).

When inserted into Word, the code inserts an unformatted, two-column table without a header. Each item in the first-level array represents a row in the resulting table; each item in a subarray contains data for a cell in the row:

// Define a matrix of data to set in the document.
var booksToRead = [["Xenophon", "Anabasis"],
  ["Plato", "Socrates' Apology"],
  ["Homer", "The Illiad"]];
// Set some data to the document as an unformatted table.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Matrix },
  function (result) {
    // Access the results, if necessary.
});

Figure 2 shows the result.

Results of Inserting Data as a Matrix
Figure 2 Results of Inserting Data as a Matrix

In addition to the matrix coercion type, we can get or set data as a table using a TableData object. This allows us to provide a little bit more formatting to the results—in this particular case, a header row. We access the header row and content of a TableData object using the headers and rows properties, respectively.

Also, with the TableData object, you can specify a subset of data to insert using the startRow and startColumn parameters. This allows you to set data into a single column of an existing five-column table, as an example. We’ll look at the startRow and startColumn parameters in greater depth in the next article in this series.

Note: If the selection in the document is a table, the selection shape must match the data being inserted (unless you specify the startRow and startColumn parameters). That is, if the data being inserted is a 2 x 2 table and the selection in the document is 3 x 2 cells in a table, then the method will fail. This also applies to inserting data as a matrix.

Like the matrix coercion type, the headers and rows properties return an array of arrays, where each item in the first array is a row of data and each item in a subarray contains one cell of data in the table, as you see in Figure 3.

Figure 3 Inserting Data into a Document as a Table

// Define some tabular data to set in the document,
// including a header row.
var booksToRead = new Office.TableData();
booksToRead.headers = [["Author", "Title"]];
booksToRead.rows = [["Xenophon", "Anabasis"],
  ["Plato", "Socrates' Apology"],
  ["Homer", "The Illiad"]];
// Set some data to the document as a table with a header.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Table },
  function (result) {
    // Access the results, if necessary.
});

Figure 4 shows the result of the code in Figure 3.

Results of Inserting Data as a Table
Figure 4 Results of Inserting Data as a Table

For the next example, we’ll insert the same data, this time formatted as HTML and using the Office.CoercionType.HTML coercion. Now we can add additional formatting to the inserted data, such as CSS styles, as shown in Figure 5.

Figure 5 Inserting Data into a Document as HTML

// Define some HTML data to set in the document,
// including header row, text formatting and CSS styles.
var booksToRead =
  "<table style='font-family:Segoe UI'>" +
    "<thead style='background-color:#283E75;color:white'>" +
      "<tr><th>Authors</th><th>Books</th></tr>" +
    "</thead>" +
    "<tbody>" +
      "<tr><td>Xenophon</td><td><u>Anabasis</u></td></tr>" +
      "<tr><td>Plato</td><td><u>Socrates' Apology</u></td></tr>" +
      "<tr><td>Homer</td><td><u>The Iliad</u></td></tr>" +
    "</tbody>" +
  "</table>";
// Set some data to the document as a table with styles applied.
Office.context.document.setSelectedDataAsync(
  booksToRead,
  { coercionType: Office.CoercionType.Html },
    function (result) {
    // Access the results, if necessary.
});

Figure 6 shows the result of the code in Figure 5.

Results of Inserting Data as HTML
Figure 6 Results of Inserting Data as HTML

Finally, we can also insert text into the document as OOXML, which lets us customize the data greatly and use many more advanced content types in Word (SmartArt or inline pictures, as two examples).

The table of data that we’ve been working with, when repre­sented as OOXML and stored in string literal, looks like the code in Figure 7 (note: only part of the table is presented, for brevity).

Figure 7 An OOXML Snippet that Represents a Word Table, Stored as a JavaScript String Literal

var newTable = "<w:tbl>" +
  "<w:tblPr>" +
    "<w:tblStyle w:val=\"TableGrid\"/>" +
    "<w:tblW w:w=\"0\" w:type=\"auto\"/>" +
    "<w:tblBorders>" +
      "<w:top w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:left w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:right w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:insideH w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "<w:insideV w:val=\"single\" w:sz=\"4\" w:space=\"0\"" +
        "w:color=\"283E75\"/>" +
      "</w:tblBorders>" +
    "<w:tblLook w:val=\"04A0\" w:firstRow=\"1\" w:lastRow=\"0\"" +
      "w:firstColumn=\"1\" w:lastColumn=\"0\""  +
      "w:noHBand=\"0\" w:noVBand=\"1\"/>" +
  "</w:tblPr>" +
  "<w:tblGrid>" +
    "<w:gridCol w:w=\"4675\"/>" +
    "<w:gridCol w:w=\"4675\"/>" +
  "</w:tblGrid>" +
  "<w:tr w:rsidR=\"00431544\" w:rsidTr=\"00620187\">" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
        "<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"283E75\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRPr=\"00236B94\""  +
        "w:rsidRDefault=\"00431544\" w:rsidP=\"00620187\">" +
        "<w:pPr>" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
        "</w:pPr>" +
        "<w:r w:rsidRPr=\"00236B94\">" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
          "<w:t>Authors</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
        "<w:shd w:val=\"clear\" w:color=\"auto\" w:fill=\"283E75\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRPr=\"00236B94\"" +
        "w:rsidRDefault=\"00431544\" w:rsidP=\"00620187\">" +
        "<w:pPr>" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
        "</w:pPr>" +
        "<w:r w:rsidRPr=\"00236B94\">" +
          "<w:rPr>" +
            "<w:b/>" +
            "<w:color w:val=\"FEFEFE\"/>" +
          "</w:rPr>" +
          "<w:t>Books</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
  "</w:tr>" +
  "<w:tr w:rsidR=\"00431544\" w:rsidTr=\"00620187\">" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRDefault=\"00431544\"" +
        "w:rsidP=\"00620187\">" +
        "<w:r>" +
          "<w:t>Xenophon</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
    "<w:tc>" +
      "<w:tcPr>" +
        "<w:tcW w:w=\"4675\" w:type=\"dxa\"/>" +
      "</w:tcPr>" +
      "<w:p w:rsidR=\"00431544\" w:rsidRDefault=\"00431544\"" +
        "w:rsidP=\"00620187\">" +
        "<w:r>" +
          "<w:t>Anabasis</w:t>" +
        "</w:r>" +
      "</w:p>" +
    "</w:tc>" +
  "</w:tr>" +
  // The rest of the code has been omitted for the sake of brevity.
"</w:tbl>";

This technique also requires a high degree of familiarity with XML, and the structures described by the OOXML standard (ECMA-376) in particular. When setting OOXML into a document, the data must be stored as a string (HTML Document objects can’t be inserted) that contains all the necessary information—including relationships and related document parts in the file format package. Thus, when inserting a more advanced content type into Word using OOXML, you must remember to manipulate the OOXML data in accordance with the best practices of using OOXML and the Open Packaging Conventions.

In Figure 8, we’ve stepped around this issue by getting the data as OOXML first, concatenating our data with the OOXML from the document (by manipulating the received data and the new data as strings) and then inserting the OOXML back into the document. (Granted, part of the reason that this code works is because we haven’t added any content that requires adding or changing any relationships or document parts in the file.)

Figure 8 Inserting Data into a Document as a Table Using OOXML

// Get the OOXML for the data at the point of insertion
// and add a table at the beginning of the selection.
Office.context.document.getSelectedDataAsync(
  Office.CoercionType.Ooxml,
  {
    valueFormat: Office.ValueFormat.Formatted,
    filterType: Office.FilterType.All
  },
  function (result) {
    if (result.status == "succeeded") {
      // Get the OOXML returned from the getSelectedDataAsync call.
      var selectedData = result.value.toString();
      // Define the new table in OOXML.
      var newTable = "<!--Details omitted for brevity.-->";
      // Find the '<w:body>' tag in the returned data—the tag
      // that represents the body content of the selection, contained
      // within the main document package part (/word/document.xml)—
      // and then insert the new table into the OOXML at that point.
      var newString = selectedData.replace(
        "<w:body>",
        "<w:body>" + newTable,
        "gi");
        // Insert the data back into the document with the table added.
        Office.context.document.setSelectedDataAsync(
          newString,
          { coercionType: Office.CoercionType.Ooxml },
          function () {
        });
    }
});

Figure 9 shows the results of the code in Figure 8.

Results of Inserting Data as OOXML
Figure 9 Results of Inserting Data as OOXML

Note: One good way to learn about how to manipulate OOXML from an app is to add the content that you want to work with using the UI (for example, inserting SmartArt by clicking Insert | Illustrations | SmartArt), getting the OOXML for the content using getSelectedDataAsync and then reading the results. See the blog post, “Inserting images with apps for Office,” at bit.ly/SeU3MS for more details.

Getting All of the Content in the File

Getting or setting data at the selec­tion point is fine, but there are scenarios where it’s necessary to get all of the content from a file. For example, an app might need to get all of the content in the document as text, parse it and then represent it in a bubble chart. As another example, an app might need to send all of the content in the file to a remote Web service for remote printing or faxing.

The JavaScript API for Office provides just the functionality for these scenarios. Using the JavaScript API, an app can create a copy of the file into which it’s inserted, break the copy into sized chunks of data or “slices” (up to 4MB), and then read the data inside the slices.

The process for getting all of the content in the file essentially includes three steps:

  1. For apps inserted into Word or PowerPoint, the app calls the Document.getFileAsync method, which returns a File object that corresponds to a copy of the file.
  2. Once the app has a reference to the file, it can call the File.getSliceAsync method to access specific slices within the file, passing in the index of the slice to get. If this is done using a for loop, the calling code must be careful about how it handles closures.
  3. Finally, the app should close the File object once it’s done with it, by calling the File.closeAsync method. Only two files are allowed to remain in memory at any time; attempting to open a third file using Document.getFileAsync raises the “An internal error has occurred” error.

In Figure 10, we get a Word document in 1KB chunks, iterate over each chunk in the file and then close the file when we’re done with it.

Figure 10 Getting All of the Content from a File as Text and Iterating over the Slices

// Get all of the content from a Word document in 1KB chunks of text.
function getFileData() {
  Office.context.document.getFileAsync(
  Office.FileType.Text,
  {
    sliceSize: 1000
  },
  function (asyncResult) {
    if (asyncResult.status === 'succeeded') {
      var myFile = asyncResult.value,
        state = {
          file: myFile,
          counter: 0,
          sliceCount: myFile.sliceCount
        };
      getSliceData(state);
    }
  });
}
// Get a slice from the file, as specified by
// the counter contained in the state parameter.
function getSliceData(state) {
  state.file.getSliceAsync(
    state.counter,
    function (result) {
    var slice = result.value,
      data = slice.data;
    state.counter++;
    // Do something with the data.
    // Check to see if the final slice in the file has
    // been reached—if not, get the next slice;
    // if so, close the file.
    if (state.counter < state.sliceCount) {
      getSliceData(state);
    }
    else {
      closeFile(state);
    }
  });
}
// Close the file when done with it.
function closeFile(state) {
  state.file.closeAsync(
    function (results) {
      // Inform the user that the process is complete.
  });
}

For more information about how to get all of the file content from within an app for Office, see the documentation page, “How to: Get the whole document from an app for PowerPoint,” at bit.ly/12Asi4x.

Getting Task Data, View Data and Resource Data from a Project

For task pane apps inserted into Project, the JavaScript API for Office includes additional methods to read data for the active project and the selected task, resource or view. The project-15.js script extends office.js and also adds selection-change events for tasks, resources and views. For example, when the user selects a task in the Team Planner view, an app can integrate and display in one place the remaining work scheduled for that task, who is available to work on it, and related projects in other SharePoint task lists or in Project Server that can affect scheduling.

A task pane app inserted into a project only has read access to the content in the project. But, because a task pane app is at heart a Web page, it can read from and write to external applications by using JavaScript and protocols such as Representational State Transfer (REST). For example, the Apps for Office and SharePoint documentation includes a sample app for Project Professional that uses jQuery with the OData reporting service in Project to compare total cost and work data for the active project with the averages for all projects in a Project Web App (see Figure 11).

A Task Pane App that Uses jQuery with an OData Reporting Service
Figure 11 A Task Pane App that Uses jQuery with an OData Reporting Service

For more information, see the documentation page, “How to: Create a Project app that uses REST with an on-premises Project Server OData service,” at bit.ly/T80W2H.

Because ProjectDocument extends the Document object, the Office.context.document object captures a reference to the active project—similar to apps inserted into other host applications. The asynchronous methods available in Project have similar signatures to the other methods in the JavaScript API for Office. For example, the getProjectFieldAsync method has three parameters:

  • fieldId: specifies the field to return in the object for the callback parameter. The Office.ProjectProjectFields enumeration includes 12 fields, such as the project GUID, start date, finish date and (if any) the Project Server URL or SharePoint task list URL.
  • asyncContext: (optional) is any user-defined type returned in the asyncResult object.
  • callback: contains a reference to a function that runs when the call returns, and contains options to handle success or failure.

As you can see in Figure 12, the methods specific to apps in Project are used similarly to apps hosted in other applications. In the script fragment, a locally defined function calls a routine that displays an error message in the app. The script doesn’t use the asyncContext parameter.

Figure 12 Getting the GUID of a Field from a Task Pane Inserted into a Project

var _projectUid = "";
// Get the GUID of the active project.
function getProjectGuid() {
  Office.context.document.getProjectFieldAsync(
    Office.ProjectProjectFields.GUID,
    function (asyncResult) {
      if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
        _projectUid = asyncResult.value.fieldValue;
        }
      else {
        // Display error message to user.
      }
    }
  );
}

Although the getProjectFieldAsync method can get only 12 fields for the general project, the getTaskFieldAsync method can get any one of 282 different fields for a task by using the ProjectTaskFields enumeration. And the getResourceFieldAsync method can get any one of 200 fields for a resource by using the ProjectResourceFields enumeration. More general methods in the ProjectDocument object include getSelectedDataAsync, which returns selected text data in any of the supported views, and getTaskAsync, which returns several items of general data for a selected task. Task pane apps can work with 16 different views in Project.

In addition, task pane apps in Project can add or remove event handlers when the user changes a view, selects a task or selects a resource.

Events in an App for Office

The JavaScript API for Office enables you to create more responsive apps through events. The event model for the API supports four key event scenarios fundamental to developing apps for Office (discussed later). Understanding these four scenarios will give you a solid grasp of the event model for the API.

That said, the event model for apps for Office is consistent throughout, so knowing the common design for event handling will complete your understanding of this important concept.

Concerning common design for events in the apps for Office API, the following objects have events associated with them:

  • Binding
  • CustomXMLPart
  • Document
  • RoamingSettings (mail apps)
  • Settings

In addition to the events associated with it, each of the objects listed has two methods for dealing with its events:

  • addHandlerAsync
  • removeHandlerAsync

Because the removeHandlerAsync method simply unsubscribes a handler from an event and because its signature is nearly identical to that of addHandlerAsync, in the next section, we’ll focus our attention on addHandlerAsync only.

Note: There’s one very important difference between the remove­HandlerAsync and addHandlerAsync methods. The handler parameter is optional for removeHandlerAsync. If unspecified, all handlers for the given event type are removed.

The AddHandlerAsync Method

The addHandlerAsync method wires up an event handler to the specified event and has the same signature for each object that implements it:

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

Now we’ll discuss the parameters for this method.

EventType Parameter The required eventType parameter takes an EventType enumeration, which tells the method which type of event to wire up.

Handler Parameter The eventType parameter is followed by the handler parameter. The handler can be either a named function or an anonymous inline function. Note that just like the event model for many programming languages, the apps for Office runtime invokes the handler and passes in an event object argument as the only parameter. Also, if you use an inline anonymous function for the handler parameter, the only way to remove the handler is to remove all handlers from the event by calling removeHandler­Async and not specifying the handler parameter.

Options Parameter Like all asynchronous functions in the apps for Office API, you can specify an object that contains optional param­eters, but for all addHandlerAsync methods, the only optional parameter that you can specify is asyncContext. It’s provided as a way to pass whatever data you want through the asynchronous method that you can retrieve inside the callback.

Callback Parameter The callback acts just as it does elsewhere throughout the apps for Office API with one significant exception: the value property of the AsyncResult object. As discussed earlier in this article, when the runtime invokes a callback, it passes in an Async­Result object and you use the value property of the AsyncResult object to get the return value of the asynchronous call. In the case of callbacks in the addHandlerAsync method, the value of the Async­Result object is always undefined.

Figure 13 demonstrates how to code the addHandlerAsync method for the DocumentSelectionChanged event (the code assumes you have a <div> element with an id attribute value of “message”).

Figure 13 Wiring up an Event Handler for the DocumentSelection­Changed Event, Using the Document.addHandlerAsync Method

Office.initialize = function (reason) {
  $(document).ready(function () {       
    Office.context.document.addHandlerAsync(
      Office.EventType.DocumentSelectionChanged, onDocSelectionChanged,
        addHandlerCallback);
      Office.context.document.addHandlerAsync(
        Office.EventType.DocumentSelectionChanged, onDocSelectionChanged2,
        addHandlerCallback2);
  });
};
function onDocSelectionChanged(docSelectionChangedArgs) {
  write("onDocSelectionChanged invoked each event.");
}
function onDocSelectionChanged2(docSelectionChangedArgs) {
  write("onDocSelectionChanged2 invoked each event.");
}
function addHandlerCallback(asyncResult) {
  write("addHandlerCallback only called once on app initialize.");
}
function addHandlerCallback2(asyncResult) {
  write("addHandlerCallback2 only called once on app initialize.");
}
function write(message) {$('#message').append(message + "\n");

When the app is initialized, the code in Figure 13 wires up the onDocSelectionChanged and onDocSelectionChanged2 handler functions to the Document­SelectionChanged event, showing that you can have more than one event handler for the same event. Both handlers simply write to the <div>, “message,” when the DocumentSelectionChanged event fires.

The calls to addHandlerAsync also include callbacks addHandlerCallback and addHandlerCallback2, respectively. The callbacks also write to the <div>, “message,” but are only called once when addHandlerAsync completes.

In the same way, you can use the addHandlerAsync method to wire up event handlers for any event in the JavaScript API for Office.

Key Scenarios in the Event Model for Apps for Office

As mentioned, in the JavaScript API for Office, there are four key event scenarios around which to order your understanding as you consider the event model for apps for Office. All events in the API will fall into one of these four key event scenarios:

  • Office.initialize events
  • Document-level selection change events
  • Binding-level selection and data changed events
  • Settings changed events

Office.initialize Events By far the most common event in the JavaScript API for Office that you’ll encounter is the Office.initialize event. The initialize event happens for every app for Office that you create. In fact, it’s the first part of your code the runtime executes.

If you look at the starter code that Visual Studio 2012 provides for any new app for Office project, you’ll see the first lines of the starter code in the ProjectName.js file for your app wire up an event handler for the Office.initialize event, as shown here:

// This function is run when the app is ready to
// start interacting with the host application;
// it ensures the DOM is ready before adding click handlers to buttons.
Office.initialize = function (reason) { /* handler code */ };

As you know from the Object model hierarchy section in the previous article, the Office object is the topmost object in the JavaScript API for Office and represents the instance of your app at run time. The Initialize event fires when the apps for Office runtime is completely loaded and ready for interaction with your app. So, the Initialize event event handler is essentially the “handshake” between your app and the runtime that has to occur before the rest of your code runs.

The function that you have to provide as the handler for the Office.initialize event takes a single argument—an InitializationReason enumeration. The Initialization­Reason enumeration has just two enumerations—Inserted and documentOpened:

  • Inserted indicates that the app is being initialized because it was just inserted into the document.
  • documentOpened means the app is being initialized because the document that already had the app inserted was just opened.

The runtime will pass in the InitializationReason enumeration as the only argument to your handler function. From there, you can branch how your code will react depending on the reason.

Here’s an idea of how this might work:

Office.initialize = function (reason) {
  // Display initialization reason.
  if (reason == "inserted")
  write("The app was just inserted.");
  if (reason == "documentOpened")
  write(
    "The app is already part of the document.");
}
// Function that writes to a div with
// id='message' on the page.
function write(message){
  document.getElementById(
  'message').innerText += message;
}

Note: The preceding code snippet assumes you have a <div> element with an id attribute value of “message.”

Interestingly, you don’t have to include anything inside the function that you supply as a handler, but the function must be present or your app will throw an error when it starts.

By the way, the event handler for the Office.initialize event is a good place to initialize other frameworks that you might use in your app, such as jQuery. Again, in the starter code that Visual Studio provides for new apps for Office projects, you’ll see something like the code in Figure 14.

Figure 14 Initializing Other Frameworks Within the Office.initialize Event Handler

Office.initialize = function (reason) {
  $(document).ready(function () {
    $('#getDataBtn').click(function () { getData('#selectedDataTxt'); });
    // If setSelectedDataAsync method is supported
    // by the host application, setDatabtn is hooked up
    // to call the method, else setDatabtn is removed.
    if (Office.context.document.setSelectedDataAsync) {
        $('#setDataBtn').click(function () { setData('#selectedDataTxt'); });
    }
    else {
      $('#setDataBtn').remove();
    }
  });
};

The jQuery .ready event is handled inside the Office.initialize event handler. This ensures the JavaScript API for Office is loaded and ready before the JQuery code calls into it.

Document-Level Selection Change Events Document-level selection change events occur when the selection in the document moves from one selection to another. For example, when the user clicks away from the current selection in a Word document to a range of text, or object or location elsewhere in the document, an event is fired at the document level for the change in selection.

The following code illustrates how to respond to a change to the current selection:

function addEventHandlerToDocument() {
    Office.context.document.addHandlerAsync(
      Office.EventType.DocumentSelectionChanged,
      MyHandler);
  }
  function MyHandler(eventArgs) {
    doSomethingWithDocument(eventArgs.document);

Binding-Level Selection and Data Changed Events Bindings in the apps for Office object model are a way to consistently access a specific area in a document (or spreadsheet) by establishing a linkage, or binding, to a uniquely named region in the document. To work with a binding, you first create one using one of the provided methods of the API. You can then refer to the specific binding that you created using its unique identifier.

Bindings also trigger events, and your app can respond to those events as needed. In particular, bindings fire an event when the selection changes within the binding area and when data changes within the binding area. The following two code snippets show how to handle changes to the selection and changes to data in a given binding (both assume you have a <div> element with an id attribute value of “message”).

Responding to the Binding.bindingSelectionChanged event:

function addEventHandlerToBinding() {
  Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingSelectionChanged,
    onBindingSelectionChanged);
}
function onBindingSelectionChanged(eventArgs) {
  write(eventArgs.binding.id + " has been selected.");
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

Responding to the Binding.bindingDataChanged event:

function addEventHandlerToBinding() {
  Office.select("bindings#MyBinding").addHandlerAsync(
    Office.EventType.BindingDataChanged, onBindingDataChanged);
}
function onBindingDataChanged(eventArgs) {
  write("Data has changed in binding: " + eventArgs.binding.id);
}
// Function that writes to a div with id='message' on the page.
function write(message){
  document.getElementById('message').innerText += message;
}

Settings Changed Events The apps for Office object model provides a way for developers to persist settings that relate to their app. The Settings object acts as a property bag where custom app settings are stored as key/value pairs. The Settings object also has an event associated with it—Settings.settingsChanged—that fires when a stored setting is changed.

For more information about the Settings.settingsChanged event, see the MSDN documentation for the JavaScript API for Office at bit.ly/U92Sbe.

Next Up: More-Advanced Topics

In this second article of the series, we reviewed the basics of getting and setting Office file content from an app for Office. We showed how to get and set selection data and how to get all of the file data. We looked at how to get project, task, view and resource data from an app for Project. Finally, we looked at the events in the JavaScript API for Office and how to code against them.

Note: We’d like to thank Jim Corbin, programming writer in the Office Division, for contributing much of the content concerning apps for Project.

Next, we’ll take a closer look at some more-advanced topics in the JavaScript API for Office: data bindings and custom XML parts.


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, Shilpa Kothari and Juan Balmori Labra
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.