Поделиться через


Модель API для конкретного приложения

В этой статье описывается использование модели API для создания надстроек в Excel, OneNote, PowerPoint, Visio и Word. Здесь представлены основные понятия, лежащие в основе использования API на основе обещаний.

Примечание.

Клиенты Outlook и Project не поддерживают эту модель. Используйте модель Common API для работы с этими приложениями. Полные сведения о доступности платформ см. в статье Доступность клиентских приложений и платформ Office для надстроек Office.

Совет

В примерах, приведенных в этой статье, используются API JavaScript для Excel, но эти понятия также применяются к API JavaScript Для OneNote, PowerPoint, Visio и Word. Полные примеры кода, показывающие, как можно использовать эти и другие понятия в различных приложениях Office, см. в разделе Примеры кода надстройки Office.

Асинхронный характер API на основе обещаний

Надстройки Office — это веб-сайты, которые отображаются в элементе управления webview в приложениях Office, таких как Excel. Этот элемент управления внедряется в приложение Office на классических платформах, таких как Office в Windows, и выполняется в iframe HTML в Office в Интернете. Из-за соображений производительности API-интерфейсы Office.js не могут синхронно взаимодействовать с приложениями Office на всех платформах. Таким образом, вызов API sync() в Office.js возвращает обещание, которое разрешается, когда приложение Office выполняет запрошенные действия чтения или записи. Кроме того, вы можете поместить в очередь несколько действий, например действия настройки свойств или вызова методов, а затем запустить их в виде пакета команд в одном вызове метода sync(), а не отправлять отдельные запросы для каждого действия. В разделах ниже описано, как сделать это, используя API run() и sync().

Функция *.run

Excel.run, OneNote.run, PowerPoint.runи Word.run выполняют функцию, которая указывает действия, выполняемые в Excel, Word и OneNote. *.run автоматически создает контекст запроса, который можно использовать для взаимодействия с объектами Office. По *.run завершении он разрешает обещание и автоматически освобождает все объекты, выделенные во время выполнения.

В следующем примере показано, как использовать шаблон Excel.run. Тот же шаблон также используется в OneNote, PowerPoint, Visio и 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));
    }
});

Контекст запроса

Приложение Office и надстройка выполняются в разных процессах. Так как они используют разные среды выполнения, надстройке требуется RequestContext объект для подключения к таким объектам Office, как листы, диапазоны, абзацы и таблицы. Этот RequestContext объект предоставляется в качестве аргумента при вызове *.run.

Прокси-объекты

Объекты JavaScript для Office, объявляемые и используемые с помощью API на основе обещаний, являются прокси-объектами. Все методы, которые вы вызываете, либо свойства, которые вы настраиваете либо загружаете, в прокси-объектах просто добавляются в очередь команд, ожидающих выполнения. Когда вы вызываете метод sync() в контексте запроса (например, context.sync()), команды, помещенные в очередь, передаются в приложение Office и выполняются. По существу, эти API ориентированы на работу с пакетами. Вы можете поместить в очередь любое количество изменений в контексте запроса, а затем вызвать метод sync(), чтобы запустить пакет команд, помещенных в очередь.

Например, во фрагменте кода ниже показано, как объявить локальный объект JavaScript Excel.Range (selectedRange) для ссылки на выделенный диапазон в книге Excel, а затем задать ряд свойств для этого объекта. Объект selectedRange представляет собой прокси-объект, поэтому свойства, заданные в этом объекте, и метод, вызываемый в этом объекте, не будут отображены в документе Excel, пока надстройка не вызовет метод context.sync().

const selectedRange = context.workbook.getSelectedRange();
selectedRange.format.fill.color = "#4472C4";
selectedRange.format.font.color = "white";
selectedRange.format.autofitColumns();

Совет по производительности: минимизируйте количество созданных прокси-объектов

Избегайте повторного создания одного и того же прокси-объекта. Вместо этого, если вам нужен одинаковый прокси-объект для нескольких операций, создайте его один раз и назначьте его переменной, а затем используйте эту переменную в своем коде.

// 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()

При вызове метода sync() в контексте запроса будет синхронизировано состояние прокси-объектов и объектов в документе Office. Метод sync() запускает любые команды, помещенные в очередь в контексте запроса, и получает значения для любых свойств, которые следует загрузить в прокси-объектах. Метод sync() выполняется асинхронно и возвращает обещание, которое разрешается по завершении работы метода sync().

В примере ниже показана пакетная функция, которая определяет локальный прокси-объект JavaScript (selectedRange), загружает свойство этого объекта, а затем использует шаблон обещаний JavaScript для вызова метода context.sync() и, соответственно, синхронизации состояния прокси-объектов и объектов в документе 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);
});

В предыдущем примере настроен параметр selectedRange, и его свойство address загружается при вызове context.sync().

Так как sync() является асинхронной операцией, всегда возвращайте Promise объект , чтобы убедиться, sync() что операция будет завершена до продолжения выполнения скрипта. Если вы используете TypeScript или JavaScript ES6+, вы можете await вызов context.sync() вместо возврата обещания.

Совет по производительности: минимизируйте количество вызовов синхронизации

В API JavaScript для Excel sync() является единственной асинхронной операцией и в некоторых обстоятельствах может выполняться медленно, особенно в случае с Excel в Интернете. Для оптимизации производительности минимизируйте количество вызовов sync(), поставив в очередь максимально возможное количество изменений до ее вызова. Дополнительные сведения об оптимизации производительности с помощью sync()см. в разделе Избегание использования метода context.sync в циклах.

load()

Чтобы можно было считывать свойства прокси-объекта, вам необходимо явно загрузить их и заполнить прокси-объект данными из документа Office, а затем вызвать метод context.sync(). Например, вы создали прокси-объект для ссылки на выделенный диапазон, а затем вам потребовалось считать свойство address выделенного диапазона. Прежде чем вы сможете считать свойство address, вам потребуется загрузить его. Чтобы запросить загрузку свойств прокси-объекта, вызовите метод load() в объекте и укажите свойства, которые необходимо загрузить. В следующем примере показана загрузка свойства Range.address для 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');
});

Примечание.

Если вы вызываете только методы или задаете свойства для прокси-объекта, вызывать метод не нужно load() . Метод load() требуется только тогда, когда вам необходимо считать свойства в прокси-объекте.

Как и запросы на задание свойств или вызов методов в прокси-объектах, запросы на загрузку свойств в прокси-объектах добавляются в очередь ожидающих команд в контексте запроса, который выполняется при следующем вызове sync() метода. Вы можете ставить в очередь столько load() вызовов в контексте запроса, сколько это необходимо.

Скалярные и навигационные свойства

Существует две категории свойств: скалярные и навигационные. К скалярным свойствам относятся назначаемые типы, такие как строки, целые числа и структуры JSON. Свойства навигации — это объекты и коллекции объектов только для чтения, которым назначаются поля вместо прямого назначения свойства. Например, элементы name и position объекта Excel.Worksheet являются скалярными свойствами, а protection и tables — свойствами навигации.

Надстройка может использовать свойства навигации в качестве пути для загрузки определенных скалярных свойств. Следующий код помещает в очередь команду load для имени шрифта, используемого объектом Excel.Range, без загрузки каких-либо других сведений.

someRange.load("format/font/name")

Вы также можете задавать скалярные свойства из свойства навигации по пути к ним. Например, вы можете задать размер шрифта для Excel.Range с помощью команды someRange.format.font.size = 10;. Чтобы задать свойство, необязательно загружать его.

Некоторые свойства объекта могут иметь то же имя, что и у другого объекта. Например, format — это свойство объекта Excel.Range, но также имеется и объект format. Таким образом, если вы выполняете такой вызов, как range.load("format"), этот вызов эквивалентен range.format.load() (нежелательной пустой load() инструкции). Чтобы избежать этой проблемы, код должен загружать только "конечные узлы" в дереве объектов.

Загрузка из коллекции

При работе с коллекцией используйте load в коллекции для загрузки свойств для каждого объекта в коллекции. Используйте load точно так же, как и для отдельного объекта в этой коллекции.

В следующем примере кода показано name , что свойство загружается и регистрируется для каждой диаграммы на листе "Пример".

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

Свойство коллекции обычно не включается items в load аргументы. Все элементы загружаются при загрузке свойств элемента. Однако если вы выполняете цикл над элементами в коллекции, но не нужно загружать какое-либо конкретное свойство элементов, вам потребуется свойство loaditems .

В следующем примере кода показано name свойство, задается для каждой диаграммы на листе "Пример".

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

При вызове load() метода для объекта или коллекции без указания параметров загружается все скалярные свойства объекта или объекта коллекции. Загрузка ненужных данных замедляет работу надстройки. Всегда явно указывайте, какие свойства следует загрузить.

Важно!

Объем данных, возвращаемых оператором load без параметров, может превышать ограничения по размерам для службы. Чтобы снизить риски для старых надстроек, служба не возвращает некоторые свойства load , не запрашивая их явным образом. Следующие свойства исключаются из таких операций загрузки.

  • Excel.Range.numberFormatCategories

ClientResult

Методы в API на основе обещаний, возвращающие примитивные типы, следуют шаблону, аналогичному парадигме load/sync . Например, Excel.TableCollection.getCount извлекает количество таблиц в коллекции. getCount ClientResult<number>возвращает значение , то есть value свойство в возвращаемом ClientResult объекте является числом. Сценарий не может получить доступ к этому значению, пока не вызовет context.sync().

Следующий код получает общее количество таблиц в книге Excel и записывает его в консоль.

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

Установка свойств объекта с вложенными свойствами навигации может быть трудоемкой задачей. В качестве альтернативы настройке отдельных свойств с помощью путей навигации, как описано ранее, можно использовать object.set() метод, доступный для объектов в API JavaScript на основе обещаний. С помощью этого метода можно задать несколько свойств объекта одновременно, передав другой объект того же типа Office.js или объект JavaScript со свойствами, структурированными как свойства объекта, для которого вызывается метод .

Следующий пример кода задает несколько свойств формата диапазона путем вызова set() метода и передачи объекта JavaScript с именами и типами свойств, которые зеркало структуру свойств в объекте Range . В этом примере предполагается, что в диапазоне 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();
});

Некоторые свойства невозможно задать напрямую

Некоторые свойства невозможно задать, хотя они и поддерживают запись. Эти свойства являются частью родительского свойства, которое должно быть задано как один объект. Это связано с тем, что родительское свойство использует вложенные свойства с определенными логическими связями. Эти родительские свойства должны быть заданы с помощью нотации литерала объекта, чтобы задать весь объект, а не отдельные вложенные свойства этого объекта. Один из примеров доступен в разделе PageLayout. Свойство zoom должно быть задано с помощью одного объекта PageLayoutZoomOptions , как показано ниже.

// PageLayout.zoom.scale must be set by assigning PageLayout.zoom to a PageLayoutZoomOptions object.
sheet.pageLayout.zoom = { scale: 200 };

В предыдущем примере нельзя напрямую присвоить zoom значение: sheet.pageLayout.zoom.scale = 200;. Эта инструкция выдает ошибку, так как zoom не загружается. Даже если zoom бы они были загружены, набор масштабирования не вступит в силу. Все контекстные операции происходят в zoom, обновляя прокси-объект в надстройке и переписывая локально установленные значения.

Это поведение отличается от свойств навигации, например Range.format. Свойства можно задать с помощью навигации format по объектам, как показано здесь.

// This will set the font size on the range during the next `content.sync()`.
range.format.font.size = 10;

Вы можете определить свойство, для которого невозможно напрямую задать его вложенные свойства, путем проверки модификатора только для чтения. Для всех свойств, доступных только для чтения, можно напрямую задать их вложенные свойства, использующиеся не только для чтения. Записываемые свойства, например PageLayout.zoom, должны быть заданы на уровне объекта. Сводка:

  • Свойство только для чтения: вложенные свойства можно задать с помощью навигации.
  • Записываемое свойство: вложенные свойства нельзя задать с помощью навигации (необходимо установить их в рамках начального назначения родительского объекта).

*Методы и свойства OrNullObject

Некоторые методы и свойства доступа создают исключение, если нужный объект не существует. Например, если для получения листа Excel указать имя листа, не существующее в книге, метод getItem() создаст исключение ItemNotFound. Библиотеки конкретных приложений позволяют коду проверять наличие сущностей документа, не требуя кода обработки исключений. В этом подходе используются *OrNullObject варианты методов и свойств. Эти варианты возвращают объект, свойство которого isNullObject имеет значение true , если указанный элемент не существует, а не создает исключение.

Например, вы можете вызвать метод getItemOrNullObject() для коллекции, такой как Worksheets, чтобы получить элемент из коллекции. Метод getItemOrNullObject() возвращает указанный элемент, если он существует. В противном случае возвращается объект, свойству isNullObject которого присвоено значение true. Затем код может оценить это свойство, чтобы определить, существует ли объект.

Примечание.

Варианты *OrNullObject никогда не возвращают значение nullJavaScript . Они возвращают обычные прокси-объекты Office. Если сущность, которую представляет объект, не существует, свойству isNullObject объекта присваивается значение true. Не проверяйте возвращаемый объект на допустимость или ложность. Это никогда не nullявляется , falseили undefined.

В следующем примере кода осуществляется попытка получить лист Excel с именем Data с помощью метода getItemOrNullObject(). Если лист с таким именем не существует, код создает новый лист. Обратите внимание, что код не загружает isNullObject свойство . Office автоматически загружает это свойство при context.sync вызове, поэтому не нужно явно загружать его с помощью чего-то вроде 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;
});

Отмена поддержки

Отмена частично поддерживается API-интерфейсами JavaScript для Office, зависящими от приложения. Это означает, что пользователи могут отменить изменения изменения, внесенные надстройками, с помощью команды отмены. Если определенный API не поддерживает отмену, стек отмены приложения очищается. Это означает, что вы не сможете отменить эффекты этого API или чего-либо еще до вызова этого API.

Поддержка отмены api продолжает расширяться, но в настоящее время не завершена. Рекомендуется не проектировать надстройку таким образом, чтобы она зависела от поддержки отмены.

См. также