Compartir a través de


Microsoft Office

Exploración de la API de JavaScript para Office: acceso a datos y eventos

Stephen Oliver
Eric Schmidt

 

Esta es la segunda parte de una serie de artículos que revisan a fondo la API de JavaScript para Office. La primera parte (disponible en msdn.microsoft.com/magazine/jj891051) entrega una amplia visión general del modelo de objetos. Este artículo retoma el punto donde terminamos en la primera parte, un recorrido detallado sobre cómo tener acceso a los contenidos en los archivos y un repaso del modelo de eventos.

A lo largo de esta serie, frecuentemente hacemos mención de la documentación de referencia de la API de JavaScript para Office. Puede encontrar la documentación oficial, los ejemplos de código y los recursos de la comunidad en las Aplicaciones para Office y el Centro para desarrolladores de SharePoint en MSDN (dev.office.com).

Obtener acceso al contenido de archivos de Office desde una aplicación para Office

La API de JavaScript para Office ofrece varias formas para tener acceso a los datos que se encuentran en un archivo de Office: podemos obtener o establecer los datos que están seleccionados actualmente o podemos obtener el archivo entero. Este nivel de acceso puede parecer sencillo y, de hecho, las dos técnicas son bastante fáciles de usar. Sin embargo, ambas técnicas ofrecen un amplio margen de flexibilidad y personalización, lo que nos ofrece un gran espectro de posibilidades para nuestras aplicaciones.

Además del acceso a los datos seleccionados o los archivos enteros, la API de JavaScript para Office también nos permite enlazar a datos y manipular partes de XML personalizadas del documento. Veremos con un poco más de detención estas técnicas para trabajar con los contenidos de Office.

Obtener y establecer datos seleccionados

Como se dijo, el objeto Document permite que las aplicaciones tengan acceso a los datos del archivo. En el caso de las aplicaciones del panel de tareas y de contenido, podemos obtener y establecer el contenido seleccionado en un archivo de Office con los métodos Document.getSelectedDataAsync y Document.setSelectedDataAsync.

Estos métodos pueden manipular varios tipos de formatos de datos que nosotros podemos controlar en la llamada. Ambos métodos, getSelectedDataAsync y setSelectedDataAsync, tienen el parámetro coercionType, que acepta una constante de la enumeración Office.CoercionType. El parámetro coercionType especifica el formato de los datos del contenido que se recibe o establece. Según el valor del parámetro coercionType, podemos seleccionar los datos como texto sin formato, una tabla, una matriz de matrices, HTML o incluso Office Open XML (OOXML) “en bruto”. (Tenga en cuenta que en el momento de redactar este artículo, solo Word 2013 permite recibir y establecer texto como HTML y OOXML.)

No siempre es necesario establecer el parámetro coercionType al usar los métodos getSelectedDataAsync y setSelectedDataAsync. El valor de coercionType se deduce del contexto siempre que sea posible. Por ejemplo, si usted pasa una cadena literal a una llamada al método setSelectedDataAsync, entonces el valor predeterminado de coercionType es “text”. Si entregáramos los mismos datos como matriz de matrices, entonces el valor predeterminado de coercionType sería “matrix”.

Entregaremos algunos ejemplos que ilustran el poder de estos métodos sencillos, principalmente con el método setSelectedDataAsync. Comenzaremos con un ejemplo que inserta un texto sencillo en un documento de Word:

// 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.
});

La figura 1 muestra el resultado.

Results of Inserting Data as Simple Text
Figura 1 Resultado de la inserción de datos como texto sencillo

Ahora cambiamos el ejemplo para insertar el texto en forma del tipo de coerción “matrix”. Matrix es una matriz de matrices que se insertó como un intervalo sencillo de celdas (Excel) o una tabla sencilla (Word).

Al insertarlo en Word, el código inserta una tabla sin formato de dos columnas sin encabezado. Cada elemento de la matriz del primer nivel representa una fila en la tabla resultante; cada elemento en una matriz secundaria contiene los datos de una celda dentro de la fila:

// 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.
});

La figura 2 muestra el resultado.

Results of Inserting Data as a Matrix
Figura 2 Resultado de la inserción de datos como “matrix”

Aparte del tipo de coerción “matrix”, podemos recibir o establecer los datos en forma de tabla, al usar un objeto TableData. Esto nos permite aplicar más formatos en el resultado; en este caso específico, una fila de encabezado. Para tener acceso a la fila de encabezado y al contenido del objeto TableData, usamos las propiedades headers y rows, respectivamente.

Además, con el objeto TableData podemos especificar un subconjunto de los datos mediante los parámetros startRow y startColumn. Esto nos permite, por ejemplo, introducir datos en una sola columna de una tabla existente con cinco columnas. En el siguiente artículo de la serie revisaremos los parámetros startRow y startColumn con más detención.

Nota: si la selección en el documento es una tabla, la forma de la selección debe coincidir con los datos que se insertan (a menos que se especifiquen los parámetros startRow y startColumn). Es decir, si los datos que se insertan tienen forma de tabla de 2 × 2 y la selección en el documento se compone de 3 × 2 celdas de una tabla, entonces el método generará un error. Lo mismo ocurre con los datos en forma de “matrix”.

Al igual que el tipo de coerción “matrix”, las propiedades headers y rows devuelven una matriz de matrices, donde cada elemento de la primera matriz es una fila de datos y cada elemento de una matriz secundaria contiene una celda de datos de la tabla, tal como se aprecia en la Figura 3.

Figura 3 Inserción de datos en un documento como una tabla

// 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.
});

En la Figura 4 se aprecian los resultados del código de la Figura 3.

Results of Inserting Data as a TableFigura 4 Resultado de la inserción de datos como una tabla

En el siguiente ejemplo insertaremos los mismos datos, pero esta vez como HTML y con la coerción Office.CoercionType.HTML. Ahora podemos agregar formatos adicionales a los datos insertados, como estilos CSS, por ejemplo, tal como se aprecia en la Figura 5.

Figura 5 Inserción de datos en un documento como 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.
});

En la Figura 6 se aprecian los resultados del código de la Figura 5.

Results of Inserting Data as HTML
Figura 6 Resultado de la inserción de datos como HTML

Por último, también podemos insertar el texto en el documento en forma de OOXML, lo que nos permite personalizar los datos considerablemente y emplear muchos tipos de contenidos más avanzados en Word (SmartArt e imágenes alineadas, por mencionar solo dos ejemplos).

Cuando la tabla de datos con la que hemos estado trabajando se representa en forma de OOXML y se almacena como cadena literal, tiene la apariencia del código que se aprecia en la Figura 7. (Nota: por motivos de espacio solo se presenta parte de la tabla.)

Figura 7 Fragmento de OOXML que representa una tabla de Word que se almacena como cadena literal en JavaScript

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>";

Esta técnica también exige muchos conocimientos de XML y, en concreto, de las estructuras que se describen en el estándar de OOXML (ECMA-376). Al establecer datos en formato OOXML en un documento, estos se deben almacenar como cadena (no se pueden insertar objetos Document HTML) que contiene toda la información necesaria, como las relaciones y las partes de los documentos relacionadas en el paquete del formato de archivo, entre otros. Por lo tanto, al insertar tipos de contenidos más avanzados en Word con OOXML, siempre debemos recordar manipular los datos de OOXML en concordancia con los procedimientos recomendados para el uso de OOXML y Open Packaging Conventions.

En la Figura 8 esquivamos el problema, al obtener los datos primero como OOXML, concatenar nuestros datos con el OOXML del documento (al manipular los datos recibidos y los datos nuevos como cadenas) y volver a insertar luego el OOXML en el documento. (Lo reconozco: este código funciona, en parte, porque no agregué ningún contenido que exigiera agregar ni modificar relaciones o partes del documento en el archivo.)

Figura 8 Inserción de datos en un documento con 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 () {
        });
    }
});

En la Figura 9 se aprecian los resultados del código de la Figura 8.

Results of Inserting Data as OOXML
Figura 9 Resultado de la inserción de datos como OOXML

Nota: una excelente forma de aprender a manipular OOXML desde una aplicación es agregar el contenido con el que deseamos trabajar desde la interfaz de usuario (para insertar SmartArt, por ejemplo, hacer clic en Insertar | Ilustraciones | SmartArt), obtener el código OOXML del contenido con getSelectedDataAsync y luego leer los resultados. Consulte el artículo “Inserción de imágenes con aplicaciones para Office” en bit.ly/SeU3MS, para obtener más información.

Obtener todo el contenido del archivo

Obtener y establecer datos en el punto de selección puede ser muy útil, pero existen situaciones donde debemos obtener todo el contenido de un archivo. Por ejemplo, una aplicación que tenga que recuperar todo el contenido de un documento como texto, analizarlo y representarlo luego en un gráfico de burbujas. Otro ejemplo podría ser una aplicación que tiene que enviar todo el contenido de un archivo a un servicio web remoto para la impresión o el envío de fax en forma remota.

La API de JavaScript para Office entrega las funciones precisas para este tipo de situaciones. Con la API de JavaScript, una aplicación puede crear una copia del archivo dentro del cual se inserta, dividir la copia en fragmentos de datos o “segmentos” (hasta 4 MB) y luego leer los datos dentro de estos segmentos.

El proceso para trasladar todo el contenido al archivo básicamente se compone de tres pasos:

  1. Para las aplicaciones insertadas en Word o PowerPoint, la aplicación llama al método Document.getFileAsync que devuelve un objeto del tipo File, que corresponde a una copia del archivo.
  2. Una vez que la aplicación tiene una referencia al archivo, puede llamar el método File.getSliceAsync para tener acceso a segmentos específicos dentro del archivo, al pasar el índice del segmento que se quiere recuperar. Si esto se realiza con un bucle for, el autor de la llamada debe tener cuidado con la manera en que trata las clausuras.
  3. Por último, la aplicación deberá cerrar el objeto File una vez que dejó de usarlo, para lo cual deberá llamar el método File.closeAsync. Solo dos archivos pueden permanecer en memoria a la vez; al intentar abrir un tercer archivo con el método Document.getFileAsync, se genera un “Error interno”.

En la Figura 10, obtenemos un documento de Word en segmentos de 1 KB, iteramos por cada segmento del archivo y luego cerramos el archivo cuando terminamos de usarlo.

Figura 10 Obtención de todo el contenido de un archivo en forma de texto e iteración por los segmentos

// 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 &lt; 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.
  });
}

Para obtener más información sobre cómo extraer el contenido de un archivo desde una aplicación para Office, consulte la página de documentación “Procedimiento para obtener el documento completo de una aplicación para PowerPoint” en bit.ly/12Asi4x.

Obtención de datos de tarea, vista y recurso desde un proyecto

Para las aplicaciones del panel de tareas insertadas en Project, la API de JavaScript para Office incluye métodos adicionales para leer los datos del proyecto activo y la tarea, recurso o vista seleccionada. El script project-15.js extiende office.js y también agrega eventos de cambio de selección para tareas, recursos y vistas. Por ejemplo, cuando el usuario selecciona una tarea en la vista del Organizador de equipo, una aplicación puede integrar y presentar en un mismo lugar el trabajo restante programado para esa tarea, quién está disponible para realizar el trabajo y los proyectos relacionados en otras listas de tarea de SharePoint o en Project Server que pueden afectar la programación.

Una aplicación de panel de tareas insertada en un proyecto solo tiene acceso de lectura al contenido del proyecto. Pero como, en el fondo, las aplicaciones de panel de tareas son páginas web, pueden realizar operaciones de lectura y escritura en aplicaciones externas mediante JavaScript y con protocolos como Representational State Transfer (REST). Por ejemplo, la documentación de las Aplicaciones para Office y SharePoint incluye una aplicación de ejemplo para Project Professional que emplea jQuery con el servicio de informes de OData en Project, para comparar el costo total y los datos de trabajo del proyecto activo con los promedios de todos los proyectos en una aplicación web de Project (consulte la Figura 11).

A Task Pane App that Uses jQuery with an OData Reporting Service
Figura 11 Una aplicación del panel de tareas que usa jQuery con un servicio de informes de OData

Para obtener más información, consulte la página de documentación “Procedimiento para crear una aplicación de Project que use REST con un servicio OData local de Project Server” en bit.ly/T80W2H.

Como ProjectDocument extiende el objeto Document, el objeto Office.context.document captura una referencia al proyecto activo, de manera similar a las aplicaciones que se insertan en otras aplicaciones host. Los métodos asincrónicos que están disponibles en Project tienen firmas parecidas a los otros métodos en la API de JavaScript para Office. El método getProjectFieldAsync, por ejemplo, tiene tres parámetros:

  • fieldId: especifica el campo que se debe devolver en el objeto para el parámetro callback. La enumeración Office.ProjectProjectFields incluye 12 campos tales como GUID del proyecto, fecha de inicio, fecha de término y, en caso de que los haya, la URL de Project Server o la URL de la lista de tareas de SharePoint.
  • asyncContext: (opcional) es cualquier tipo definido por el usuario que se devuelve en el objeto asyncResult.
  • callback: contiene una referencia a la función que se ejecuta cuando se devuelve la llamada y contiene opciones para tratar con los casos satisfactorios y los errores.

Como podrá apreciar en la Figura 12, los métodos propios de las aplicaciones en Project se usan en forma similar a las aplicaciones que están hospedadas en otras aplicaciones. En el fragmento del script, una función definida en forma local llama una rutina que presenta un mensaje de error en la aplicación. El script no usa el parámetro asyncContext.

Figura 12 Obtención del GUID de un campo desde un panel de tareas insertado en un 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.
      }
    }
  );
}

Aunque el método getProjectFieldAsync solo puede recibir 12 campos para el proyecto general, el método getTaskFieldAsync es capaz de recibir cualquiera de los 282 campos diferentes para una tarea, al usar la enumeración ProjectTaskFields. Y el método getResourceFieldAsync puede obtener cualquiera de los 200 campos como recurso, al usar la enumeración ProjectResourceFields. Algunos métodos más generales del objeto ProjectDocument son getSelectedDataAsync, que devuelve datos de texto seleccionado en cualquiera de las vistas admitidas, y getTaskAsync, que devuelve varios elementos de datos generales para una tarea seleccionada. Las aplicaciones del panel de tareas pueden funcionar con 16 vistas diferentes en Project.

Además, las aplicaciones del panel de tareas en Project pueden agregar o eliminar controladores de eventos cuando el usuario cambia una vista, selecciona una tarea o selecciona un recurso.

Eventos en una aplicación para Office

La API de JavaScript para Office permite usar eventos para crear aplicaciones que responden mejor. El modelo de eventos para la API reconoce cuatro categorías de eventos clave que son fundamentales para el desarrollo de las aplicaciones para Office (se analizan más adelante). Si entiende estas cuatro categorías, podrá entender a fondo el modelo de eventos de la API.

Por otro lado, el modelo de eventos para las aplicaciones para Office es completamente coherente, así que basta con conocer el diseño común del control de eventos para terminar de comprender este concepto importante.

En cuanto al diseño común de los eventos en la API de las aplicaciones para Office, los siguientes objetos tienen eventos asociados:

  • Enlaces
  • CustomXMLPart
  • Documento
  • RoamingSettings (aplicaciones de correo)
  • Configuración

Aparte de los eventos asociados, cada uno de los objetos mencionados tiene dos métodos para tratar con sus eventos:

  • addHandlerAsync
  • removeHandlerAsync

Como el método removeHandlerAsync simplemente cancela la suscripción de un controlador de un evento y como su firma es prácticamente idéntica a la del método addHandlerAsync, en la siguiente sección nos concentraremos en addHandlerAsync solamente.

Nota: existe una diferencia importante entre los métodos remove­HandlerAsync y addHandlerAsync. El parámetro handler es opcional en el caso de removeHandlerAsync. Cuando no se especifica, todos los controladores con el tipo de evento dado se eliminan.

Método AddHandlerAsync

El método addHandlerAsync conecta un controlador de eventos con el evento especificado y tiene la misma firma para todos los objetos que lo implementan:

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

Ahora analizaremos los parámetros de este método.

Parámetro eventType El parámetro eventType requerido recibe una enumeración EventType, que le indica al método el tipo de evento que debe conectar.

Parámetro handler Después del parámetro eventType viene el parámetro handler. El controlador puede ser una función con nombre o una función anónima en línea. Tenga en cuenta que al igual que en el modelo de eventos de muchos lenguajes de programación, el tiempo de ejecución de las aplicaciones para Office invoca el controlador y pasa un argumento de objeto de evento como el único parámetro. Además, si usamos una función anónima en línea en el parámetro handler, la única forma de eliminar el controlador es eliminar todos los controladores del evento con una llamada al método removeHandler­Async y dejar el parámetro handler sin especificar.

Parámetro options Como en todas las funciones asincrónicas de la API de las aplicaciones para Office, podemos especificar un objeto que contiene parámetros opcionales. Pero para todos los métodos addHandlerAsync, el único parámetro opcional que se puede especificar es asyncContext. Sirve para poder pasar cualquier tipo de datos que deseemos a través del método asincrónico que podemos recuperar dentro de la devolución de llamada.

Parámetro callback La devolución de llamada simplemente actúa igual que en todo el resto de la API de las aplicaciones para Office, con una excepción importante: la propiedad value del objeto AsyncResult. Tal como se indicó más arriba, cuando el tiempo de ejecución invoca una devolución de llamada, pasa un objeto Async­Result, y empleamos la propiedad value del objeto AsyncResult para obtener el valor devuelto por la llamada asincrónica. En el caso de las devoluciones de llamada en el método addHandlerAsync, el valor del objeto Async­Result siempre es indefinido.

En la Figura 13 se ilustra cómo programar el método addHandlerAsync para el evento DocumentSelectionChanged (el código supone que tenemos un elemento <div> con un atributo id con el valor “message”).

Figura 13 Conexión de un controlador del evento DocumentSelection­Changed, mediante el método Document.addHandlerAsync

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");

Cuando la aplicación se inicializa, el código de la Figura 13 conecta las funciones controladoras onDocSelectionChanged y onDocSelectionChanged2 con el evento Document­SelectionChanged, lo que demuestra que podemos tener más de un controlador de eventos para el mismo evento. Ambos controladores simplemente escriben en el elemento <div> “message” cuando se activa el evento DocumentSelectionChanged.

Las llamadas a addHandlerAsync también incluyen las devoluciones de llamada addHandlerCallback y addHandlerCallback2, respectivamente. Las devoluciones de llamada también escriben en el elemento <div> “message”, pero solo se llaman una vez que addHandlerAsync finaliza.

Del mismo modo, podemos usar el método addHandlerAsync para conectar controladores de evento para cualquier evento en la API de JavaScript para Office.

Categorías clave en el modelo de eventos en las aplicaciones para Office

Tal como se mencionó, en la API de JavaScript para Office existen cuatro categorías de eventos clave en torno a los cuales debemos ordenar nuestras ideas cuando tenemos en cuenta el modelo de eventos para las aplicaciones para Office. Todos los eventos de la API caen dentro de una de estas cuatro categorías de eventos clave:

  • Evento Office.initialize
  • Eventos de cambios de selección en el nivel de documento
  • Eventos de cambios de datos y de selección en el nivel de enlaces
  • Eventos de cambios en Settings

Evento Office.initialize Lejos el evento más común en la API de JavaScript para Office con el que nos encontramos es Office.initialize. El evento initialize se genera para cada aplicación de Office que creamos. De hecho, es la primera parte de nuestro código que ejecuta el tiempo de ejecución.

Si revisa el código de inicio que Visual Studio 2012 entrega para todos los proyectos nuevos de aplicaciones para Office, verá que las primeras líneas del código de inicio en el archivo ProjectName.js de la aplicación conectan un controlador del evento Office.initialize, del siguiente modo:

// 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 */ };

Como podrá recordar de la sección sobre la jerarquía del modelo de objetos del artículo anterior, el objeto Office es el primer objeto de la API de JavaScript para Office y representa la instancia de nuestra aplicación en tiempo de ejecución. El evento Initialize se activa cuando el tiempo de ejecución de las aplicaciones para Office se cargó completamente y está listo para interactuar con la aplicación. Por lo tanto, el controlador del evento Initialize es esencialmente el “protocolo de enlace” entre nuestra aplicación y el tiempo de ejecución, que debe producirse antes de que se ejecute el resto de nuestro código.

La función que debemos proporcionar como el controlador del evento Office.initialize recibe un solo argumento, una enumeración InitializationReason. La enumeración Initialization­Reason solo tiene dos elementos, Inserted y documentOpened:

  • Inserted indica que la aplicación se inicializó porque se acaba de insertar en el documento.
  • documentOpened significa que la aplicación se inicializó porque el documento que ya tenía insertada la aplicación se acaba de abrir.

El tiempo de ejecución pasará la enumeración InitializationReason como el único argumento a nuestra función controladora. De ahí en adelante, podemos ramificar el código según la reacción que tengamos frente a esta razón.

Esta es una idea sobre cómo podría funcionar:

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;
}

Nota: el fragmento anterior supone que tenemos un elemento <div> con un atributo id que tiene el valor “message.”

Curiosamente, no debemos incluir nada dentro de la función que entregamos como controlador, pero la función tiene que estar presente: de lo contrario, la aplicación generará un error al inicio.

Por cierto, el controlador del evento Office.initialize es un lugar excelente para inicializar otros marcos que queramos usar en la aplicación como, por ejemplo, jQuery. También aquí en el código de inicio que Visual Studio entrega para los proyectos nuevos de las aplicaciones para Office veremos algo parecido al código de la Figura 14.

Figura 14 Inicialización de otro marco dentro del controlador del evento Office.initialize

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();
    }
  });
};

El evento .ready de jQuery se controla dentro del controlador del evento Office.initialize. Así nos aseguramos de que la API de JavaScript para Office se cargue y esté lista antes de que el código de JQuery realice llamadas a esta.

Eventos de cambios de selección en el nivel de documento Los eventos de cambios de selección en el nivel de documento se generan cuando la selección en el documento cambia de una selección a otra. Por ejemplo, cuando el usuario tiene seleccionada una palabra y hace clic en un intervalo de texto diferente o algún objeto u otra ubicación dentro del documento, se activa un evento en el nivel de documento para el cambio en la selección.

El siguiente código ilustra la forma de responder frente a un cambio en la selección actual:

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

Eventos de cambios de datos y selección en el nivel de enlaces Los enlaces en el modelo de objetos de las aplicaciones para Office otorgan una forma coherente de tener acceso a un área específica de un documento (u hoja de cálculo), al establecer un vínculo o enlace a una región con un nombre único dentro del documento. Para trabajar con un enlace, primero debemos crear uno con uno de los métodos que entrega la API. Luego podemos referirnos al enlace específico que creamos mediante el identificador único.

Los enlaces también activan eventos y nuestra aplicación puede responder a estos eventos cuando haga falta. En concreto, los enlaces activan un evento cuando la selección y los datos cambian dentro del área de enlace. Los siguientes dos fragmentos muestran cómo controlar los cambios en la selección y los datos en un enlace determinado (ambos suponen que tenemos un elemento <div> con un atributo id que tiene el valor “message”).

Respuesta al evento Binding.bindingSelectionChanged:

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;
}

Respuesta al evento Binding.bindingDataChanged:

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;
}

Eventos de cambios en Settings El modelo de objetos de las aplicaciones para Office ofrece una forma para que los desarrolladores persistan las configuraciones que tienen que ver con su aplicación. El objeto Settings actúa como contenedor de propiedades, donde las configuraciones personalizadas de la aplicación se almacenan como pares clave-valor. El objeto Settings también tiene asociado un evento, llamado Settings.settingsChanged, que se activa cuando se cambia una configuración almacenada.

Para obtener más información sobre el evento Settings.settingsChanged, consulte la documentación de MSDN de la API de JavaScript para Office en bit.ly/U92Sbe.

Siguiente entrega: temas más avanzados

En este segundo artículo de la serie revisamos los aspectos básicos de cómo obtener y establecer contenidos de archivos de Office desde una aplicación para Office. Vimos cómo obtener y establecer datos de una selección y cómo obtener los datos de un archivo completo. Vimos cómo obtener datos de recurso de proyecto, tarea y vista desde una aplicación para Project. Y, por último, revisamos los elementos de la API de JavaScript para Office y vimos cómo usarlos en nuestro código.

Nota: queremos agradecer a Jim Corbin, escritor de programación de la división de Office por contribuir gran parte del contenido sobre las aplicaciones para Project.

En el siguiente artículo revisaremos más detenidamente algunos temas más avanzados de la API de JavaScript para Office: el enlace de datos y partes de XML personalizadas.

Stephen Oliver es escritor de programación de la división de Office y desarrollador profesional certificado de Microsoft (SharePoint 2010). Escribe la documentación para desarrolladores para los servicios Excel y los servicios de automatización de Word, junto con la documentación para desarrolladores de servicios de automatización de PowerPoint. Ayudó a curar y diseñar el sitio de Excel Mashup site en ExcelMashup.com.

Eric Schmidt es escritor de programación de la división de Office. Ha creado varios ejemplos de código para aplicaciones de Office, incluido el popular ejemplo de Persistencia de la configuración personalizada. Además, ha escrito artículo y creado vídeos acerca de otros productos y tecnologías dentro de la capacidad de programación de Office.

Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Mark Brewster, Shilpa Kothari y Juan Balmori Labra
Mark Brewster se licenció en Matemáticas y ciencias informáticas en la Universidad de Arizona en 2008 y trabaja como desarrollador para Microsoft desde hace cuatro años. Anda en bicicleta por diversión y por dinero, le gusta beber cerveza y escuchar música envasada.

Shilpa Kothari (Bhavsar) es ingeniera en software en prueba en Microsoft. Ha trabajado en varios productos de Microsoft, como Bing Mobile, Visual Studio y Office. Le apasiona el aseguramiento de la calidad en el software y la experiencia de usuario, y se la puede ubicar en shilpak@microsoft.com.

Juan Balmori Labra es Director de programa y durante los últimos tres años trabaja en la API de JavaScript para Microsoft Office. Anteriormente trabajó en la versión de Office 2010, donde lanzó los Servicios de conectividad empresarial y Duet. Antes de realizar su sueño de trasladarse a Redmond, Juan trabajó en Microsoft México como Arquitecto principal para la Práctica de consulta para el sector público.