Modelo de API específico de la aplicación
En este artículo se describe cómo usar el modelo de API para compilar complementos en Excel, OneNote, PowerPoint, Visio y Word. Introduce los conceptos básicos que son fundamentales para usar las API basadas en promesas.
Nota:
Este modelo no es compatible con los clientes de Outlook o Project. Use el modelo de API común para trabajar con esas aplicaciones. Para ver todas las notas de disponibilidad de plataformas, consulte Disponibilidad de plataforma y aplicaciones cliente de Office para complementos de Office.
Sugerencia
En los ejemplos de esta página se usan las API de JavaScript de Excel, pero los conceptos también se aplican a las API de JavaScript de OneNote, PowerPoint, Visio y Word. Para ver ejemplos de código completos que muestran cómo podría usar estos y otros conceptos en varias aplicaciones de Office, consulte Ejemplos de código de complementos de Office.
Naturaleza asincrónica de las API basadas en promesas
Los complementos de Office son sitios web que aparecen dentro de un control de vista web dentro de aplicaciones de Office, como Excel. Este control se inserta dentro de la aplicación de Office en plataformas basadas en escritorio, como Office en Windows, y se ejecuta dentro de un iframe HTML en Office en la Web. Debido a las consideraciones de rendimiento, las API de Office.js no pueden interactuar sincrónicamente con las aplicaciones de Office en todas las plataformas. Por lo tanto, la llamada de API sync()
en Office.js devuelve una promesa que se resuelve cuando la aplicación de Office completa las acciones de lectura o escritura solicitadas. Además, puede poner en cola varias acciones, como establecer propiedades o invocar métodos, y ejecutarlas como un lote de comandos con una sola llamada a sync()
, en lugar de enviar una solicitud independiente para cada acción. Las secciones siguientes describen cómo hacerlo mediante las API run()
y sync()
.
Función *.run
Excel.run
, OneNote.run
, PowerPoint.run
y Word.run
ejecute una función que especifique las acciones que se realizarán en Excel, Word y OneNote. *.run
crea automáticamente un contexto de solicitud que puede usar para interactuar con objetos de Office. Cuando *.run
finaliza, se resuelve una promesa y se liberan automáticamente los objetos que fueron asignados en tiempo de ejecución.
En el ejemplo siguiente se muestra cómo usar Excel.run
. El mismo patrón también se usa con OneNote, PowerPoint, Visio y Word.
Excel.run(function (context) {
// Add your Excel JS API calls here that will be batched and sent to the workbook.
console.log('Your code goes here.');
}).catch(function (error) {
// Catch and log any errors that occur within `Excel.run`.
console.log('error: ' + error);
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
Contexto de solicitud
La aplicación de Office y el complemento se ejecutan en diferentes procesos. Puesto que usan entornos de tiempo de ejecución diferentes, los complementos requieren un objeto RequestContext
para conectar el complemento con objetos de Office como hojas de cálculo, rangos, párrafos y tablas. Este objeto RequestContext
se proporciona como un argumento al llamar a *.run
.
Objetos proxy
Los objetos de JavaScript de Office que declare y use con las API basadas en promesas son objetos proxy. Los métodos que invoque o las propiedades que establezca o cargue en objetos proxy simplemente se agregan a una cola de comandos pendientes. Cuando se llama al método sync()
en el contexto de la solicitud (por ejemplo, context.sync()
), los comandos en cola se envían a la aplicación de Office y se ejecutan. Estas API están centradas principalmente en lotes. Puede poner tantos cambios como quiera en el contexto de la solicitud y, después, llamar al método sync()
para ejecutar el lote de comandos en cola.
Por ejemplo, el siguiente fragmento de código declara el objeto JavaScript local Excel.Range, selectedRange
, para hacer referencia a un rango seleccionado en el libro de Excel y, después, establece algunas propiedades en el objeto. El objeto selectedRange
es un objeto proxy, por lo que las propiedades establecidas y el método que se invoca en ese objeto no se reflejarán en el documento de Excel hasta que el complemento llame a context.sync()
.
const selectedRange = context.workbook.getSelectedRange();
selectedRange.format.fill.color = "#4472C4";
selectedRange.format.font.color = "white";
selectedRange.format.autofitColumns();
Sugerencia de rendimiento: minimizar el número de objetos proxy creados
Evite crear repetidamente el mismo objeto proxy. En su lugar, si necesita el mismo objeto proxy para más de una operación, créelo una vez y asígnelo a una variable y, a continuación, use esa variable en el código.
// BAD: Repeated calls to .getRange() to create the same proxy object.
worksheet.getRange("A1").format.fill.color = "red";
worksheet.getRange("A1").numberFormat = "0.00%";
worksheet.getRange("A1").values = [[1]];
// GOOD: Create the range proxy object once and assign to a variable.
const range = worksheet.getRange("A1");
range.format.fill.color = "red";
range.numberFormat = "0.00%";
range.values = [[1]];
// ALSO GOOD: Use a "set" method to immediately set all the properties
// without even needing to create a variable!
worksheet.getRange("A1").set({
numberFormat: [["0.00%"]],
values: [[1]],
format: {
fill: {
color: "red"
}
}
});
sync()
Llamar al método sync()
en el contexto de solicitud sincroniza el estado entre los objetos proxy y los objetos en el documento de Office. El método sync()
ejecuta todos los comandos que están en cola en el contexto de la solicitud y recupera los valores de las propiedades que deben cargarse en los objetos proxy. El método sync()
se ejecuta de forma asincrónica y devuelve una promesa, que se resuelve cuando el método sync()
finaliza.
En el ejemplo siguiente se muestra una función de proceso por lotes que define un objeto de proxy local de JavaScript (selectedRange
), carga una propiedad de ese objeto y, después, usa el modelo de las promesas de JavaScript para llamar a context.sync()
para sincronizar el estado entre los objetos proxy y los objetos en el documento de Excel.
await Excel.run(async (context) => {
const selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
await context.sync();
console.log('The selected range is: ' + selectedRange.address);
});
En el ejemplo anterior, se establece selectedRange
y su propiedad address
se carga cuando se llama a context.sync()
.
Como sync()
es una operación asincrónica, siempre debe devolver el objeto Promise
para asegurarse de que la operación sync()
se completa antes de que el script se siga ejecutando. Si usa TypeScript o ES6+ JavaScript, puede await
la llamada context.sync()
en lugar de devolver la promesa.
Sugerencia de rendimiento: minimizar el número de llamadas de sincronización
En la API de JavaScript de Excel, sync()
es la única operación asincrónica y puede ser lenta en algunas circunstancias, especialmente en el caso de Excel para la Web. Para optimizar el rendimiento, minimice el número de llamadas a sync()
poniendo en cola tantos cambios como sea posible antes de llamarla. Para más información sobre cómo optimizar el rendimiento con sync()
, consulte Evite usar el método context.sync en bucles.
load()
Para poder leer las propiedades de un objeto proxy, debe cargar explícitamente las propiedades para rellenar el objeto proxy con datos del documento de Office y, después, llamar a context.sync()
. Por ejemplo, si crea un objeto proxy para hacer referencia a un rango seleccionado y, después, quiere leer la propiedad address
del rango seleccionado, tendrá que cargar la propiedad address
antes de que se pueda leer. Para solicitar que se carguen las propiedades de un objeto proxy, llame al método load()
en el objeto y especifique las propiedades que se van a cargar. En el ejemplo siguiente se muestra la propiedad Range.address
que se carga para myRange
.
await Excel.run(async (context) => {
const sheetName = 'Sheet1';
const rangeAddress = 'A1:B2';
const myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('address');
await context.sync();
console.log (myRange.address); // ok
//console.log (myRange.values); // not ok as it was not loaded
console.log('done');
});
Nota:
Si solo llama a métodos o establece propiedades en un objeto proxy, no es necesario llamar al load()
método . El método load()
solo es necesario cuando quiere leer las propiedades de un objeto proxy.
Al igual que las solicitudes para establecer propiedades o invocar métodos en objetos proxy, las solicitudes para cargar propiedades en objetos proxy se agregan a la cola de comandos pendientes en el contexto de la solicitud, que se ejecutará la próxima vez que llame al sync()
método. Puede poner en cola tantas load()
llamadas en el contexto de solicitud como sea necesario.
Propiedades escalares y de navegación
Hay dos tipos de categorías para las propiedades:escalarynavegación. Las propiedades escalares son tipos asignables como cadenas, enteros y estructuras JSON. Las propiedades de navegación son objetos de solo lectura y colecciones de objetos que tienen sus campos asignados, en lugar de asignar directamente la propiedad. Por ejemplo, los miembros name
y position
en el objeto Excel.Worksheet son propiedades de objetos escalares, mientras que protection
y tables
son propiedades de navegación.
Su complemento puede usar propiedades de navegación como una ruta de acceso para cargar propiedades escalares específicas. El siguiente código pone en cola un comando load
para el nombre de la fuente usada por un objeto Excel.Range
, sin cargar otra información.
someRange.load("format/font/name")
También puede establecer las propiedades escalares de una propiedad de navegación recorriendo la ruta de acceso. Por ejemplo, puede establecer el tamaño de fuente para un Excel.Range
mediante someRange.format.font.size = 10;
. No es necesario que cargue la propiedad antes de establecerlo.
Tenga en cuenta que es posible que algunas de las propiedades de un objeto tengan el mismo nombre que otro objeto. Por ejemplo, format
es una propiedad dentro del objeto Excel.Range
, pero format
en sí es un objeto. Por lo tanto, si realiza una llamada como range.load("format")
, equivale a range.format.load()
(una instrucción load()
vacía no intencionada). Para evitar esto, el código solo debería cargar los "nodos hoja" en un árbol de objeto.
Carga desde una colección
Cuando trabaje con una colección, use load
en la colección para cargar las propiedades de cada objeto de la colección. Use load
exactamente como lo haría para un objeto individual de esa colección.
El código de ejemplo siguiente muestra la name
propiedad que se carga y registra para cada gráfico de la hoja de cálculo "Ejemplo".
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sample");
const chartCollection = sheet.charts;
// Load the name property on every chart in the chart collection.
chartCollection.load("name");
await context.sync();
chartCollection.items.forEach((chart) => {
console.log(chart.name);
});
});
Normalmente no se incluye la items
propiedad de la colección en los load
argumentos. Todos los elementos se cargan si se cargan propiedades de elementos. Sin embargo, si va a recorrer en bucle los elementos de la colección, pero no es necesario cargar ninguna propiedad determinada de los elementos, debe tener load
la items
propiedad .
En el código de ejemplo siguiente se muestra la name
propiedad que se establece para cada gráfico de la hoja de cálculo "Sample".
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sample");
const chartCollection = sheet.charts;
// Load the items property from the chart collection to set properties on individual charts.
chartCollection.load("items");
await context.sync();
chartCollection.items.forEach((chart, index) => {
chart.name = `Sample chart ${index}`;
});
});
Llamar a load
sin parámetros (no se recomienda)
Si llama al método load()
en un objeto (o colección) sin especificar ningún parámetro, se cargarán todas las propiedades escalares del objeto o los objetos de la colección. La carga de datos innecesarios ralentizará el complemento. Debería especificar siempre de forma explícita qué propiedades se cargarán.
Importante
La cantidad de datos devueltos por una instrucción load
sin parámetros puede superar los límites de tamaño del servicio. Para reducir los riesgos para los complementos anteriores, load
no devuelve algunas propiedades sin solicitarlas explícitamente. Las siguientes propiedades se excluyen de estas operaciones de carga.
Excel.Range.numberFormatCategories
ClientResult
Los métodos de las API basadas en promesas que devuelven tipos primitivos tienen un patrón similar al paradigma load
/sync
. Por ejemplo, Excel.TableCollection.getCount
obtiene el número de tablas de la colección. getCount
devuelve un ClientResult<number>
, lo que significa que la propiedad value
en el ClientResult
de retorno es un número. El script no puede acceder a ese valor hasta que se llama a context.sync()
.
El siguiente código obtiene el número total de tablas en un libro de Excel y registra ese número en la consola.
const tableCount = context.workbook.tables.getCount();
// This sync call implicitly loads tableCount.value.
// Any other ClientResult values are loaded too.
await context.sync();
// Trying to log the value before calling sync would throw an error.
console.log (tableCount.value);
set()
Establecer las propiedades de un objeto con las propiedades de navegación anidadas puede resultar engorroso. Como alternativa a establecer propiedades individuales mediante rutas de navegación como se describió anteriormente, puede usar el método object.set()
, que está disponible en objetos de la API de JavaScript basada en promesas. Con este método, puede establecer varias propiedades de un objeto a la vez pasando otro objeto del mismo tipo de Office o un objeto JavaScript con propiedades estructuradas como las propiedades del objeto en el que se llama al método.
En el ejemplo de código siguiente se establecen varias propiedades de formato de un intervalo llamando al set()
método y pasando un objeto JavaScript con nombres de propiedad y tipos que reflejan la estructura de propiedades del Range
objeto. En este ejemplo se supone que hay datos en el rango B2:E2.
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B2:E2");
range.set({
format: {
fill: {
color: '#4472C4'
},
font: {
name: 'Verdana',
color: 'white'
}
}
});
range.format.autofitColumns();
await context.sync();
});
Algunas propiedades no pueden establecerse directamente.
Algunas propiedades no se pueden establecer, a pesar de que se puedan escribir. Estas propiedades forman parte de una propiedad principal que debe establecerse como un único objeto. Esto se debe a que esa propiedad principal se basa en que las subpropiedades tienen relaciones lógicas y específicas. Estas propiedades primarias se deben establecer con la notación literal del objeto para establecer todo el objeto, en lugar de establecer las subpropiedades individuales del objeto. Un ejemplo de esto se encuentra en PageLayout. La zoom
propiedad debe establecerse con un único objeto PageLayoutZoomOptions , como se muestra aquí.
// PageLayout.zoom.scale must be set by assigning PageLayout.zoom to a PageLayoutZoomOptions object.
sheet.pageLayout.zoom = { scale: 200 };
En el ejemplo anterior, no podría asignar zoom
directamente un valor: sheet.pageLayout.zoom.scale = 200;
. Esa instrucción produce un error porque zoom
no se carga. Incluso si zoom
se cargase, el conjunto de escala no tendría efecto. Todas las operaciones de contexto suceden en zoom
, actualizando el objeto proxy en el complemento y sobrescribiendo los valores establecidos de forma local.
Este comportamiento difiere de propiedades de navegación como Range.format. Las propiedades de format
se pueden establecer mediante la navegación de objetos, como se muestra aquí.
// This will set the font size on the range during the next `content.sync()`.
range.format.font.size = 10;
Puede identificar una propiedad que no puede tener sus subpropiedades establecidas directamente comprobando su modificador de solo lectura. Todas las propiedades de solo lectura pueden tener sus subpropiedades que no sean de solo lectura establecidas directamente. Las propiedades que se pueden escribir, como PageLayout.zoom
, deben establecerse con un objeto en ese nivel. En resumen:
- Propiedad de solo lectura: se pueden establecer subpropiedades a través de la navegación.
- Propiedad que se puede escribir: no se pueden establecer subpropiedades a través de la navegación (deben establecerse como parte de la asignación de objetos primarios inicial).
*Métodos y propiedades de OrNullObject
Algunos métodos y propiedades de acceso producirán una excepción cuando el objeto deseado no exista. Por ejemplo, si intenta obtener una hoja de cálculo de Excel especificando un nombre de hoja de cálculo que no está en el libro, el método getItem()
produce una excepción ItemNotFound
. Las bibliotecas específicas de una aplicación proporcionan una manera para que el código compruebe si existen entidades de documento sin requerir código de control de excepciones. Esto se consigue usando las variantes *OrNullObject
de métodos y propiedades. Estas variaciones devuelven un objeto cuya propiedad isNullObject
se establece en true
si el elemento especificado no existe, en lugar de producir una excepción.
Por ejemplo, puede llamar al método getItemOrNullObject()
en una colección como Worksheets para recuperar un elemento de la colección. El método getItemOrNullObject()
devuelve el elemento especificado, si existe; de lo contrario, devuelve un objeto cuya propiedad isNullObject
está establecida en true
. Después, el código puede evaluar esta propiedad para determinar si el objeto existe.
Nota:
Las variaciones *OrNullObject
nunca devuelven el valor de JavaScript null
. Devuelven objetos proxy de Office normales. Si la entidad que representa el objeto no existe, la propiedad isNullObject
del objeto se establece en true
. No pruebe el objeto devuelto para comprobar nulidad o falsedad. Nunca es null
, false
o undefined
.
En el ejemplo de código siguiente se intenta recuperar una hoja de cálculo de Excel denominada "Datos" mediante el método getItemOrNullObject()
. Si no existe una hoja de cálculo con ese nombre, se crea una nueva hoja. Tenga en cuenta que el código no carga la propiedad isNullObject
. Office carga automáticamente esta propiedad cuando se llama a context.sync
, por lo que no necesita cargarla explícitamente con algo como dataSheet.load('isNullObject')
.
await Excel.run(async (context) => {
let dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
await context.sync();
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
// Set `dataSheet` to be the second worksheet in the workbook.
dataSheet.position = 1;
});
Pila de deshacer de la aplicación
Cuando se procesa una API específica de la aplicación, se borra la pila de deshacer de la aplicación. Esto significa que no puede deshacer los cambios realizados antes de cualquier acción realizada por un complemento (a menos que ese complemento solo use LAS API comunes o no interactúe con el archivo). Lo mismo sucede con los cambios realizados por el complemento.