Это вторая в серии статей, посвященных глубокому изучению JavaScript API for Office. В первой статье (http://www.oszone.net/20529/JavaScript-API-Office) был дан широкий обзор объектной модели. Эта статья продолжается с того места, где мы остановились в прошлый раз, — с детального рассмотрения того, как обращаться к содержимому файла, и обзора модели событий.
На протяжении всей серии мы часто ссылаемся на справочную документацию по JavaScript API for Office. Вы можете найти официальную документацию, образцы кода и ресурсы сообщества в разделе Apps for Office and SharePoint Developer Center на сайте MSDN (dev.office.com).
Доступ к содержимому файла Office из другого приложения для Office
JavaScript API for Office предоставляет несколько основных способов доступа к данным в файле Office: можно либо получать/задавать текущие выделенные данные, либо получать весь файл. Этот уровень доступа к данным может показаться простым и действительно обе методики несложны в использовании. Однако каждая методика обеспечивает высокую гибкость и адаптацию, что дает массу возможностей вашим приложениям.
Помимо доступа к выделенным данным или ко всему файлу, JavaScript API for Office также обеспечивает связывание с данными или манипулирование собственными XML-фрагментами в документе. Давайте подробнее рассмотрим эти методики работы с контентом Office.
Получение и задание выделенных данных
Как упоминалось ранее, объект Document дает приложению доступ к данным в файле. В случае приложений области задач и контента мы можем получать или задавать выделенный контент в файле Office, используя методы Document.getSelectedDataAsync и Document.setSelectedDataAsync.
Эти два метода позволяют манипулировать несколькими типами форматов данных. Оба принимают параметр coercionType, в котором передается одна из констант из перечисления Office.CoercionType. Параметр coercionType указывает формат данных, в котором нужно получить или установить контент. В зависимости от его значения можно выбирать в качестве данных чистый текст, таблицу, матрицу, HTML или даже «исходный» формат Office Open XML (OOXML). (Заметьте, что на момент публикации этой статьи получение и задание текста в виде HTML или OOXML поддерживается только Word 2013.)
При использовании getSelectedDataAsync и setSelectedDataAsync не всегда обязательно указывать coercionType. По возможности он распознается логически из контекста. Например, если вы передаете строковый литерал в вызове setSelectedDataAsync, то coercionType по умолчанию будет «text». Если бы вы передали те же данные в виде массива массивов, coercionType по умолчанию был бы «matrix».
Мы дадим некоторые примеры того, насколько мощными могут быть эти простые методы, но в основном будем использовать метод setSelectedDataAsync. Начнем с кода, который вставляет простой текст в документ Word:
// Определяем какие-либо данные для вставки в документ
var booksToRead = "Anabasis by Xenophon; \n" +
"Socrates' Apology by Plato; \n" +
"The Illiad by Homer.";
// Задаем эти данные как простой текст
Office.context.document.setSelectedDataAsync(
booksToRead,
{ coercionType: Office.CoercionType.Text },
function (result) {
// Обращаемся к результатам при необходимости
});
Результат показан на рис. 1.
Рис. 1. Результаты вставки данных как простого текста
Теперь мы изменим пример так, чтобы вставить текст как матрицу. Матрица — это массив массивов, вставляемых как простой диапазон ячеек (Excel) или простая таблица (Word).
При вставке в Word код включает неформатированную таблицу без шапки и с двумя полями. Каждый элемент в массиве первого уровня представляет строку в конечной таблице, а каждый элемент в подмассиве содержит данные для ячейки в этой строке:
// Определяем матрицу данных для записи в документ
var booksToRead = [["Xenophon", "Anabasis"],
["Plato", "Socrates' Apology"],
["Homer", "The Illiad"]];
// Задаем эти данные как неформатированную таблицу
Office.context.document.setSelectedDataAsync(
booksToRead,
{ coercionType: Office.CoercionType.Matrix },
function (result) {
// Обращаемся к результатам при необходимости
});
Результат показан на рис. 2.
Рис. 2. Результат вставки данных как матрицы
Помимо типа приведения (coercion type) к матрице, можно получать или задавать данные как таблицу, используя объект TableData. Это позволяет немного расширить возможности форматирования конечного результата — в данном конкретном случае можно создать шапку таблицы (header row). Для обращения к этой строке шапки и содержимому объекта TableData используются свойства headers и rows соответственно.
Кроме того, с помощью объекта TableData можно указывать подмножество данных для вставки, используя параметры startRow и startColumn. Это позволяет, например, помещать данные в один столбец существующей таблицы с пятью полями. Мы подробнее рассмотрим параметры startRow и startColumn в следующей статье из этой серии.
Примечание: Если выделением (selection) в документе является таблица, форма выделения должна соответствовать вставляемым данным (если только вы не указываете параметры startRow и startColumn). То есть если вставляемые данные являются таблицей 2 × 2, а выделение в документе представляет собой ячейки 3 × 2 в таблице, этот метод завершится неудачей. То же самое относится к вставке данных как матрице.
Как и тип приведения «матрица», свойства headers и rows возвращают массив массивов, где каждый элемент в первом массиве — строка данных, а каждый элемент в подмассиве содержит одну ячейку данных в таблице, как показано на рис. 3.
Рис. 3. Вставка данных в документ как таблицы
// Определяем некие табличные данные для вставки в документ,
// включая строку заголовка (шапку)
var booksToRead = new Office.TableData();
booksToRead.headers = [["Author", "Title"]];
booksToRead.rows = [["Xenophon", "Anabasis"],
["Plato", "Socrates' Apology"],
["Homer", "The Illiad"]];
// Присваиваем эти данные документу как таблицу с шапкой
Office.context.document.setSelectedDataAsync(
booksToRead,
{ coercionType: Office.CoercionType.Table },
function (result) {
// Обращаемся к результатам при необходимости
});
Результат выполнения кода с рис. 3. представлен на рис. 4.
Рис. 4. Результаты вставки данных как таблицы
В следующем примере мы вставим те же данные, но на этот раз отформатированные как HTML и воспользуемся приведением Office.CoercionType.HTML. Теперь мы можем включить дополнительное форматирование во вставленные данные, такие как CSS-стили (рис. 5).
Рис. 5. Вставка данных в документ как HTML
// Определяем некие HTML-данные для вставки в документ, включая
// строку заголовка, форматирование текста и CSS-стили
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>";
// Вставляем эти данные в документ как таблицу
// с примененными стилями
Office.context.document.setSelectedDataAsync(
booksToRead,
{ coercionType: Office.CoercionType.Html },
function (result) {
// Обращаемся к результатам при необходимости
});
Результаты выполнения кода с рис. 5 показаны на рис. 6.
Рис. 6. Результаты вставки данных как HTML
Наконец, можно вставить текст в документ и как OOXML, что открывает широкие возможности в оформлении данных и позволяет использовать гораздо более сложные типы контента в Word (например, SmartArt или подставляемые картинки [inline pictures]).
Таблица данных, с которой мы работали, представив ее в виде OOXML и сохранив как строковый литерал, показана на рис. 7 (заметьте, что для краткости здесь дана лишь часть этой таблицы).
Рис. 7. Фрагмент OOXML, представляющий таблицу Word, сохраненную как строковый литерал 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>" +
// Остальной код опущен для краткости
"</w:tbl>";
Эта методика требует также высокой квалификации в области XML и, в частности, структур, описываемых стандартом OOXML (ECMA-376). При вставке OOXML в документ данные должны храниться в виде строки (HTML-объекты Document вставлять нельзя), которая содержит всю необходимую информацию, в том числе взаимосвязи и связанные части документа в пакете файлового формата (file format package). Таким образом, при вставке в Word более сложного типа контента, использующего OOXML, вы должны манипулировать OOXML-данными в соответствии с правилами и рекомендациями по применению OOXML и Open Packaging Conventions.
На рис. 8 мы обошли эту проблему, сначала получив данные как OOXML, затем сцепив наши данные с этим OOXML из документа (обрабатывая полученные и новые данные как строки), а потом вставив OOXML обратно в документ. (Признаемся, что отчасти этот код работает потому, что мы не добавляли никакого контента, требующего введения или удаления каких-либо зависимостей или частей документа в файле.)
Рис. 8. Вставка данных в документ как таблицы с использованием OOXML
// Получаем OOXML для данных в точке вставки и добавляем
// таблицу в начало выделения
Office.context.document.getSelectedDataAsync(
Office.CoercionType.Ooxml,
{
valueFormat: Office.ValueFormat.Formatted,
filterType: Office.FilterType.All
},
function (result) {
if (result.status == "succeeded") {
// Получаем OOXML, возвращаемый getSelectedDataAsync
var selectedData = result.value.toString();
// Определяем новую таблицу в OOXML
var newTable = "<!--Details omitted for brevity.-->";
// Находим тег '<w:body>' в возвращенных данных;
// этот тег представляет тело контента в выделении,
// содержащемся в основной части пакета документа
// (/word/document.xml), а затем вставляем новую таблицу
// в OOXML в этой точке
var newString = selectedData.replace(
"<w:body>",
"<w:body>" + newTable,
"gi");
// Вставляем данные обратно в документ
// с добавленной таблицей
Office.context.document.setSelectedDataAsync(
newString,
{ coercionType: Office.CoercionType.Ooxml },
function () {
});
}
});
Результаты выполнения кода с рис. 8 показаны на рис. 9.
Рис. 9. Результаты вставки данных как OOXML
Примечание: Один из хороших способов научиться тому, как манипулировать OOXML из какого-либо приложения — добавить контент, с которым вам нужно работать, через UI (например, вставив SmartArt выбором Insert | Illustrations | SmartArt), получить OOXML для этого контента с помощью getSelectedDataAsync, а затем изучить результаты. Подробнее на эту тему см. публикацию в блоге «Inserting images with apps for Office» по ссылке bit.ly/SeU3MS.
Получение всего содержимого из файла
Получение или установка данных в точке выделения работает нормально, но бывают ситуации, где нужно получать все содержимое из файла. Так, приложению может понадобиться весь контент в документе в виде текста, его разбор и последующее представление в виде схемы с кружками и стрелками (bubble chart). Другой пример: приложению требует отправить весь контент из файла удаленному веб-сервису для удаленной печати или рассылки по факсу.
JavaScript API for Office предоставляет функциональность и для таких сценариев. С помощью JavaScript API приложение может создать копию файла, разбить ее на фиксированные порции данных (размером до 4 Мб), а затем читать данные в этих фрагментах.
Процесс получения всего содержимого файла, по сути, сводится к следующим трем этапам.
- Приложение, вставляемое в Word или PowerPoint, вызывает метод Document.getFileAsync, который возвращает объект File, соответствующий копии файла.
- Как только приложение получает ссылку на файл, оно может вызывать метод File.getSliceAsync, чтобы обращаться к конкретным порциям файла, передавая индекс нужной порции. Если это делается в цикле for, будьте внимательны к тому, как вызывающий код обрабатывает замыкания (closures).
- Наконец, приложение должно закрывать объект File по окончании работы с ним, вызывая метод File.closeAsync. В любом момент в памяти могут оставаться лишь два файла; попытка открыть третий файл с помощью Document.getFileAsync вызовет ошибку «An internal error has occurred».
Один из хороших способов научиться тому, как манипулировать OOXML из какого-либо приложения — добавить контент, с которым вам нужно работать, через UI.
На рис. 10 мы получаем документ Word порциями по 1 Кб, перебираем весь файл такими порциями и закрываем файл по окончании работы с ним.
Рис. 10. Получение всего содержимого из файла в виде текста и перебор порций
// Получаем все содержимое документа Word
// порциями по 1 Кб в виде текста
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);
}
});
}
// Получаем порцию файла, как указывается счетчиком,
// содержащимся в параметре state
function getSliceData(state) {
state.file.getSliceAsync(
state.counter,
function (result) {
var slice = result.value,
data = slice.data;
state.counter++;
// Что-то делаем с данными
// Проверяем, не достигнута ли последняя порция в файле;
// если нет, получаем следующую порцию, а если да,
// закрываем файл
if (state.counter < state.sliceCount) {
getSliceData(state);
}
else {
closeFile(state);
}
});
}
// Закрываем файл по окончании работы с ним
function closeFile(state) {
state.file.closeAsync(
function (results) {
// Уведомляем пользователя о завершении процесса
});
}
Подробнее о том, как получить все содержимое файла из приложения для Office, см. страницу документации «How to: Get the whole document from an app for PowerPoint» по ссылке bit.ly/12Asi4x.
Получение из Project данных задачи, представления и ресурса
В случае приложений области задач (task pane apps), вставляемых в Project, JavaScript API for Office предлагает дополнительные методы, позволяющие считывать данные для активного проекта и выбранной задачи, ресурса или представления. Скрипт project-15.js расширяет office.js и, кроме того, добавляет события смены выбора для задач, ресурсов и представлений. Например, когда пользователь выбирает задачу в представлении Team Planner, приложение может интегрировать и отображать в одном месте оставшуюся работу, запланированную для этой задачи, список сотрудников, способных выполнить эту работу, и связанные проекты в других списках задач SharePoint или Project Server, которые могут повлиять на график работ.
Приложение области задач, вставленное в проект, имеет доступ к содержимому проекта только для чтения.
Приложение области задач, вставленное в проект, имеет доступ к содержимому проекта только для чтения. Но, поскольку это приложение лежит в основе веб-страницы, оно может читать и записывать данные во внешние приложения, используя JavaScript и такие протоколы, как Representational State Transfer (REST). Например, в документацию на Apps for Office and SharePoint включено приложение-пример для Project Professional, которое использует jQuery в сочетании с OData-сервисом подготовки отчетов в Project для сравнения данных по общим затратам и объемам работы по активному проекту со средними показателями для всех проектов в Project Web App (рис. 11).
Рис. 11. Приложение области задач, использующее jQuery в сочетании с OData-сервисом подготовки отчетов
Подробнее на эту тему см. страницу документации «How to: Create a Project app that uses REST with an on-premises Project Server OData service» по ссылке bit.ly/T80W2H.
Поскольку ProjectDocument расширяет объект Document, объект Office.context.document захватывает ссылку на активный проект — по аналогии с приложениями, вставляемыми в другие хост-приложения. Асинхронные методы, доступные в Project, имеют сигнатуры, похожие на сигнатуры других методов в JavaScript API for Office. Например, у метода getProjectFieldAsync три параметра:
- fieldId указывает в объекте поле, возвращаемое для параметра callback. Перечисление Office.ProjectProjectFields включает 12 полей, такие как GUID проекта, дата начала, дата окончания и (если таковые есть) URL для Project Server или URL списка задач в SharePoint;
- asyncContext (необязательный) — любой пользовательский тип, возвращаемый в объекте asyncResult;
- callback содержит ссылку на функцию, которая выполняется при возврате управления этим методом и содержит код для обработки успешного или неудачного выполнения метода.
Как видно на рис. 12, методы, специфичные для приложений в Project, используются примерно так же, как в приложениях, размещаемых в других хост-приложениях. Во фрагменте скрипта локально определенная функция вызывает процедуру, которая отображает в приложении сообщение об ошибке. В этом скрипте не используется параметр asyncContext.
Рис. 12. Получение GUID поля из области задач, вставленной в Project
var _projectUid = "";
// Получаем GUID активного проекта
function getProjectGuid() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.GUID,
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
_projectUid = asyncResult.value.fieldValue;
}
else {
// Отображаем пользователю сообщение об ошибке
}
}
);
}
Хотя метод getProjectFieldAsync может получать только 12 полей для универсального проекта, метод getTaskFieldAsync способен получить любое из 282 полей для задачи, используя перечисление ProjectTaskFields, а метод getResourceFieldAsync — любое из 200 полей для ресурса, используя перечисление ProjectResourceFields. К более универсальным методам в объекте ProjectDocument относятся getSelectedDataAsync, который возвращает выделенные текстовые данные в любом поддерживаемом представлении, и getTaskAsync, который возвращает несколько элементов универсальных данных для выбранной задачи. Приложения области задач могут работать с 16-ю разными представлениями в Project.
Кроме того, приложения области задач в Project могут добавлять или удалять обработчики событий, когда пользователь изменяет представление, выбирает задачу или ресурс.
События в приложении для Office
JavaScript API for Office позволяет создавать за счет использования событий более «отзывчивые» приложения. Модель событий для этого API поддерживает четыре ключевых сценария, фундаментальных в разработке приложений для Office (об этом — позже). Понимание этих четырех сценариев даст вам четкое представление об этой модели событий.
Модель событий полностью согласованная, поэтому, изучив общий шаблон обработки событий, вы полностью поймете эту важную концепцию.
В отношении общего шаблона событий в приложениях для Office API следующие объекты имеют связанные с ними события:
- Binding;
- CustomXMLPart;
- Document;
- RoamingSettings (почтовые приложения);
- Settings.
Помимо событий, сопоставленных с ними, каждый из перечисленных объектов имеет два метода для работы с его событиями:
- addHandlerAsync;
- removeHandlerAsync.
Так как метод removeHandlerAsync просто отменяет подписку обработчика на некое событие и поскольку его сигнатура почти одинакова таковой у addHandlerAsync, в следующем разделе мы сосредоточимся только на addHandlerAsync.
Примечание: Между методами removeHandlerAsync и addHandlerAsync есть одно очень важное различие. Параметр handler не обязателен для removeHandlerAsync. Если он не указан, удаляются все обработчики событий данного типа.
Метод AddHandlerAsync
Метод addHandlerAsync подключает обработчик событий к определенному событию и имеет одинаковую сигнатуру в каждом объекте, который его реализует:
objectName.addHandlerAsync(eventType, handler [, options], callback);
Давайте обсудим параметры этого метода.
Параметр eventType Обязательный параметр. Принимает перечисление EventType, которое сообщает методу, к какому типу события требуется подключение.
Параметр handler Этот параметр может быть либо именованной функцией, либо анонимной, подставляемой в код функцией. Заметьте: как и в модели событий большинства языков программирования, исполняющая среда приложений для Office вызывает обработчик и передает ему объект события как единственный параметр. Кроме того, если вы используете подставляемую в код анонимную функцию в качестве параметра handler, единственный способ удалить данный обработчик — убрать все обработчики этого события вызовом removeHandlerAsync без параметра handler.
Параметр options Как и во всех асинхронных функциях в приложениях для Office API, вы можете указать объект, содержащий необязательные параметры, но в методах addHandlerAsync единственный необязательный параметр — asyncContext. Он позволяет передавать любые данные через асинхронный метод, которые вы можете получать в обратном вызове.
Параметр callback Этот параметр действует точно так же, как и везде в Office API, с одним важным исключением: вы не можете использовать свойство value объекта AsyncResult. Как обсуждалось ранее в этой статье, когда исполняющая среда запускает обратный вызов, она передает объект AsyncResult и с помощью его свойства value вы получаете значение, возвращаемое асинхронным вызовом. В случае обратных вызовов в методе addHandlerAsync значение объекта AsyncResult всегда является неопределенным.
На рис. 13 демонстрируется, как написать метод addHandlerAsync для события DocumentSelectionChanged (в коде предполагается, что у вас есть элемент <div> с атрибутом id, значение которого — «message»).
Рис. 13. Подключение обработчика для события DocumentSelectionChanged через метод 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");
При инициализации приложения код на рис. 13 подключает функции-обработчики onDocSelectionChanged и onDocSelectionChanged2 к событию DocumentSelectionChanged, показывая, что для одного и того же события может быть не один обработчик. Когда срабатывает событие DocumentSelectionChanged, оба обработчика просто пишут в <div> сообщение.
Вызовы addHandlerAsync также включают обратные вызовы addHandlerCallback и addHandlerCallback2 соответственно. Эти обратные вызовы тоже записывают сообщение в <div>, но вызываются лишь раз, когда addHandlerAsync завершается.
В том же духе вы можете использовать метод addHandlerAsync для подключения обработчиков к любым событиям в JavaScript API for Office.
Ключевые сценарии в модели событий в приложениях для Office
Как упоминалось, в JavaScript API for Office существуют четыре ключевых сценария, вокруг которых группируются события в модели. Все события в этом API подпадают под одну из следующих четырех категорий:
- события Office.initialize;
- события смены выделения уровня документа;
- события изменения данных и выделения уровня привязки;
- события изменения настроек.
События Office.initialize До сих пор в JavaScript API for Office вы чаще всего встречались с событием Office.initialize. Событие initialize происходит в каждом приложении для Office, которое вы создаете. Фактически это первая часть вашего кода, выполняемая исполняющей средой.
Если вы посмотрите на начальный код, предоставляемый Visual Studio 2012 для любого нового приложения в проекте Office, то заметите, что первые строки начального кода в файле ProjectName.js для вашего приложения обеспечивают подключение обработчика к событию Office.initialize:
// Эта функция выполняется, когда приложение готово
// к взаимодействию с хост-приложением; она гарантирует
// готовность DOM до добавления обработчиков щелчков кнопок
Office.initialize = function (reason) { /* код обработчика */};
Как вы знаете из предыдущей статьи (раздела по иерархии объектной модели), Office является объектом самого верхнего уровня в JavaScript API for Office и представляет экземпляр вашего приложения в период выполнения. Событие Initialize срабатывает, когда исполняющая среда приложения для Office полностью загружена и готова к взаимодействию с вашим приложением. Поэтому обработчик события Initialize, по сути, является средством «установления связи» между вашим приложением и исполняющей средой, и происходить все это должно до того, как начнет выполняться остальная часть вашего кода.
Функция, которую вы должны предоставить в качестве обработчика события Office.initialize, принимает единственный аргумент: перечисление InitializationReason. Это перечисление содержит всего два члена: Inserted и documentOpened:
- Inserted указывает, что приложение инициализируется, так как оно было только что вставлено в документ;
- documentOpened означает, что приложение инициализируется, так как только что был открыт документ, в который это приложение уже было вставлено.
Исполняющая среда передаст перечисление InitializationReason как единственный аргумент вашей функции-обработчику. С этого момента вы можете решать, как ваш код будет реагировать на конкретную причину.
Вот пример того, как это могло бы работать:
Office.initialize = function (reason) {
// Отображаем причину инициализации
if (reason == "inserted")
write("The app was just inserted.");
if (reason == "documentOpened")
write(
"The app is already part of the document.");
}
// Функция, которая записывает в div с id='message' на странице
function write(message){
document.getElementById(
'message').innerText += message;
}
Примечание: В предыдущем фрагменте кода предполагается, что у вас есть элемент <div> с атрибутом id, значение которого — «message».
Любопытно, что вы не обязаны что-то включать в тело функции, предоставляемой в качестве обработчика, но эта функция в любом случае должна присутствовать в вашем приложении, а иначе при его запуске возникнет ошибка.
Кстати, обработчик события Office.initialize — хорошее место для инициализации других инфраструктур, которые вы, возможно, используете в своем приложении, например jQuery. И вновь в начальном коде, предоставляемом Visual Studio для новых приложений в проектах Office, вы увидите нечто вроде того, что показано на рис. 14.
Рис. 14. Инициализация других инфраструктур в обработчике события Office.initialize
Office.initialize = function (reason) {
$(document).ready(function () {
$('#getDataBtn').click(function () { getData('#selectedDataTxt'); });
// Если метод setSelectedDataAsync поддерживается
// хост-приложением, setDatabtn подключается к вызову
// метода, а иначе setDatabtn удаляется
if (Office.context.document.setSelectedDataAsync) {
$('#setDataBtn').click(function () { setData('#selectedDataTxt'); });
}
else {
$('#setDataBtn').remove();
}
});
};
jQuery-событие .ready обрабатывается внутри обработчика события Office.initialize. Это гарантирует загрузку JavaScript API for Office и его готовность до вызовов из jQuery-кода.
События смены выделения уровня документа Эти события возникают при перемещении в документе с одного выделения на другое. Например, когда пользователь щелкает кнопку мыши в документе Word и переходит из текущего выделения к диапазону текста или объекту, или к любому месту в документе, на уровне документа генерируется событие смены выделения.
Следующий код иллюстрирует, как реагировать на смену текущего выделения:
function addEventHandlerToDocument() {
Office.context.document.addHandlerAsync(
Office.EventType.DocumentSelectionChanged,
MyHandler);
}
function MyHandler(eventArgs) {
doSomethingWithDocument(eventArgs.document);
События изменения данных и выделения уровня привязки В приложениях для объектной модели Office привязки (bindings) — это способ согласованного доступа к конкретной области документа (или электронной таблицы) за счет установления связи, или связывания, с уникально именованной областью документа. Чтобы работать с привязкой, вы сначала создаете ее, используя один из предоставляемых API методов. Затем вы можете ссылаться на конкретную привязку, созданную вами, по ее уникальному идентификатору.
Привязки также инициируют события, и ваше приложение при необходимости может реагировать на них. В частности, привязки генерируют событие при изменении выделения в связанной области и при изменении данных в ней. Следующие два фрагмента кода показывают, как обрабатывать изменения в выделении и данных в данной привязке (в обоих фрагментах кода предполагается, что у вас есть элемент <div> с атрибутом id, значение которого — «message»).
Реагируем на событие Binding.bindingSelectionChanged:
function addEventHandlerToBinding() {
Office.select("bindings#MyBinding").addHandlerAsync(
Office.EventType.BindingSelectionChanged,
onBindingSelectionChanged);
}
function onBindingSelectionChanged(eventArgs) {
write(eventArgs.binding.id + " has been selected.");
}
// Функция, которая записывает в div с id='message' на странице
function write(message){
document.getElementById('message').innerText += message;
}
Реагируем на событие 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);
}
// Функция, которая записывает в div с id='message' на странице
function write(message){
document.getElementById('message').innerText += message;
}
События изменения настроек Приложения для объектной модели Office позволяют разработчикам сохранять настройки, относящиеся к их приложениям. Объект Settings действует как контейнер свойств, в котором параметры приложения хранятся в виде пар «ключ-значение». С объектом Settings также связано событие Settings.settingsChanged, которое срабатывает, если изменяется сохраненный параметр.
Подробнее о событии Settings.settingsChanged см. документацию MSDN на JavaScript API for Office по ссылке bit.ly/U92Sbe.
Что дальше: более сложная тематика
Во второй статье из этой серии мы обсудили основы получения и установки содержимого файлов Office из приложения для Office. Мы показали, как получить и задать данные выделения и как получить все данные из файла. Вы узнали, как обращаться к данным проекта, задачи, представления и ресурса из приложения для Project. Наконец, были рассмотрены события в JavaScript API for Office и то, как кодировать с использованием событий.