Microsoft Office

探索新的 JavaScript API for Office

Stephen Oliver
Eric Schmidt

 

本文是深入介绍 Microsoft Office 2013 中新引入的 JavaScript API for Office 的系列文章的第一篇。 本文假设您熟悉用于 Office 的应用程序。 若您不熟悉,请参考 MSDN 文档页“用于 Office 的应用程序概述”(bit.ly/12nBWHG),那里提供了对该 API 的大体概述和一般性介绍。

本文和此系列中其他文章虽不详尽,但深入介绍了该 API,触及到一些很关键的方面,能帮助您对 Office API 应用程序的工作方式获得更为丰富翔实的了解。

在此第一篇文章中,我们讨论用于 Office 对象模型的应用程序。 第二部分着重介绍访问 Office 文件内容的核心任务,并考察事件模型。 第三部分将考察数据绑定的概念,并介绍有关使用自定义 XML 部件的基本知识。 作为此系列文章的最后一篇,第四部分将着重介绍邮件应用程序。

在整个系列中,我们经常引用 Office API 应用程序文档。 您可以在 MSDN 上的“Office 和 SharePoint 应用程序开发人员中心”(dev.office.com) 找到官方文档、示例代码和社区资源。

JavaScript API for Office 概述

JavaScript API for Office 包含完整的对象模型。 该 API 包含在一组 JavaScript 文件中,其中第一个文件是 office.js。 应用程序必须包含对 office.js 文件的引用才能使用 JavaScript API for Office。 office.js 文件在加载时会装入它操作所必需的其他脚本,包括主机环境和区域设置字符串所需的脚本。 幸运的是,您可以使用内容交付网络 (CDN) 添加对 office.js 的引用,而不必随您的应用程序一起部署 office.js 文件的副本。 例如:

 

<!-- When deploying an app, you should always
  load the CDN version of the office.js file.
-->
<script src=
  "https://appsforoffice.microsoft.com/lib/1.0/hosted/office.js">
</script>

该对象模型基于以下几个目标而设计:

  1. “一次编写,随处运行。”该对象模型必须可扩展,不能局限于某个特定主机应用程序,而应围绕在多个主机应用程序中使用的功能而构建。 应用程序以一致的方式访问特定于主机的功能。
  2. 跨平台。 在这个目标列表中,兼容性也是非常重要的一条;因此对象模型不能局限于某个特定的 Office 版本。 此外,相同的代码同样能在 Office 客户端应用程序的受支持 Web 应用程序版本上运行。 例如,一个用于 Excel 的应用程序可以像在 Excel 客户端应用程序中一样在 Excel Web App 中运行。
  3. 性能和安全性。 它应该能够实现最佳性能,以使应用程序在用户眼中尽量不显唐突。 此外,该 JavaScript API 还经过专门设计,无需自动运行 Office 应用程序就可直接与文档内容交互,从而提高了解决方案的稳定性和安全性。

该 JavaScript API 的另一个重要目标是将 Web 开发人员吸引到 Office 平台。 因此,该对象模型在构建时就考虑到了现代 Web 编程方法。 您在结合 JavaScript API for Office 创建应用程序时,可以利用您目前对其他 JavaScript 库(如 jQuery)所掌握的技能和知识。

异步编程模式

正前所述,在 Office API 应用程序的设计中,性能是很重要的目标。 设计人员增强 API 性能的方法之一是大量使用异步函数。

使用异步函数可避免应用程序在执行过程中因某函数长时间不返回而出现阻塞的情况。 调用异步函数时,程序执行并不等待该函数返回, 而是在该异步函数仍在执行的同时继续。 这使用户能够在应用程序可能仍在运行的同时继续使用 Office 文档。

本节介绍以下一些有助于理解 Office API 应用程序异步设计的要点:

  • Office API 应用程序中异步函数的通用签名
  • 在异步函数中使用可选参数
  • AsyncResult 对象在异步函数中的角色

我们将逐一讨论这些要点。

Office API 应用程序中异步函数的通用签名 Office API 应用程序中的所有异步函数都有相同的命名约定和相同的基本签名。 每个异步函数的名称都以“Async”结尾,例如: Document.getSelectedDataAsync。

所有异步函数的签名都遵循以下基本模式:

functionNameAsync(
    requiredParameters,
    [, options], [callback]);

必需参数后面另有两个参数: 一个包含可选参数的对象和一个回调函数,两者始终都是可选的。

异步函数中的可选参数 异步函数签名中的可选 JavaScript 对象是用分号分隔的键/值对的集合,其中的键是参数名称,值是您想对该参数使用的数据。 键/值对的顺序无关紧要,只要参数名称正确即可。 每个异步函数的 MSDN 文档详细说明了可在该特定函数的选项对象中使用哪些参数。

例如,Document.setSelectedDataAsync 方法具有 Office 应用程序中所有异步函数通用的相同基本签名:

Office.context.document.setSelectedDataAsync(
  data [, options], callback);

与该 API 中所有异步函数一样,Document.set­SelectedDataAsync 也有一个选项对象,其中包含可选参数,但其选项对象的参数与该 API 中其他异步函数的参数不同,因为此函数的用途是设置数据。 因此,Document.setSelectedDataAsync 的可选参数与设置数据有关:

  • coercionType: 一个指定所插入数据格式(文本、HTML、OOXML、表或矩阵)的 CoercionType 枚举
  • asyncContext: 一个用户定义的对象,在作为回调函数唯一参数传递给该函数的 AsyncResult 对象中无变化返回。

这一概念同样适用于其他所有异步函数。

您可以在异步函数调用中以内嵌对象文本的方式提供包含可选参数的对象,也可以先创建对象,然后为该参数传递该对象。 下面是两段示例代码,显示了使用 Document.setSelectedDataAsync 函数提供选项对象的两种方式。

传递内嵌选项参数:

function setData(data) {
  Office.context.document.setSelectedDataAsync(data, {
  coercionType: Office.CoercionType.Text }  
  );
}

在 JavaScript 对象中传递选项参数:

function setData(data) {
  var options = { coercionType: Office.CoercionType.Text };
  Office.context.document.setSelectedDataAsync(data, options );
}

AsyncResult 对象在异步函数中的角色 JavaScript API for Office 中异步函数的通用签名中的第三个参数是可选的回调参数。 该回调参数的作用正如其名: 它是您所提供的供异步操作完成时调用的一个函数。 当然,您在异步函数调用中提供命名函数或内嵌匿名函数都可以。 此处需要注意很重要的一点,那就是 AsyncResult 对象对于回调函数的角色。

当运行时调用回调函数时,它会将 Async­Result 对象作为回调函数唯一的参数传入。 AsyncResult 对象包含有关异步操作的信息,如: 操作是否成功;发生了什么错误(如果有);以及异步函数的返回值(如果有)。 实际上,在返回某种数据或对象的所有异步函数中,AsyncResult 都是可以获得返回值的唯一方式。 您可以使用 AsyncResult.value 属性实现这一点。

例如,以下代码片段获取文档的大小,并将文档大小显示在应用程序 UI 的指定 HTML 元素中。 要获取文件大小,首先需要获取 Document.getFileAsync 方法通过 AsyncResult.value 属性返回的文件对象。 以下是操作方法:

function getFileData(elementId) {
  Office.context.document.getFileAsync(Office.FileType.Text,
  function (asyncResult) {
    if (asyncResult.status === 'succeeded') {
      var myFile = asyncResult.value;
      $(elementId).val(myFile.size);
    }
  });
}

getFileData 函数调用 Document.getFileAsync 方法,调用时指定它应以文本方式返回文件内容。 然后,它使用传入匿名函数回调的 AsyncResult 对象的值属性来获取对 File 对象的引用。 然后,它使用 File 对象的 size 属性在指定元素中显示文件的大小。 同样,您将使用 AsyncResult.value 属性来获取 Office API 应用程序中任何异步函数的返回值。

您可以在本系列下一篇文章中读到关于 Document.getFileAsync 方法的更多内容。

对象模型层次结构

JavaScript API for Office 旨在提供跨各个 Office 版本的兼容性,以及跨不同主机应用程序的对称性。 为了实现这些目标,JavaScript API 使用了一个简单的对象模型,该模型层次分明,并不直接局限于任何特定主机应用程序。 该对象模型包含一组用于与 Office 文档交互的有针对性的功能,其可用范围界定为使用它们的应用程序类型(任务窗格、内容或邮件应用程序)。

图 1 是 JavaScript API for Office 中对象层次结构中顶层的概图(请注意,此处并未显示完整对象模型)。 该图明确展现了 Office、Context、Document、Settings、Mailbox 和 RoamingSettings 对象之间的关系。

The Object Model Hierarchy in the JavaScript API for Office
图 1 JavaScript API for Office 中的对象模型层次结构

每个主机应用程序(Word、Excel、Excel Web App、PowerPoint、Project、Outlook 和 Outlook Web App)都可以使用该 API 中的一部分功能。 例如,对象模型中大约 40% 的功能都仅与 Outlook 和 Outlook Web App 中专用的邮件应用程序相关。 对象模型的另一部分则可用于与自定义 XML 部件(只在 Word 2013 中提供)交互。

图 2 显示了对特定主机应用程序可用的功能。

图 2 JavaScript API for Office 中适用于主机应用程序的功能

功能 Word Excel/Excel Web App PowerPoint Outlook/Outlook Web App Project
以文本、表和矩阵形式获取/设置数据 全部 全部 仅文本   仅文本
Settings(设置) 全部 全部 全部 (RoamingSettings)  
获取文件 全部   仅压缩文件    
绑定 全部 全部      
自定义 XML 部件 全部        
HTML 和 OOXML 全部        
邮箱       全部  

对象模型中的共享对象 JavaScript API for Office 有一个明确的入口点:Office 对象。该入口点对所有类型的应用程序并在所有主机应用程序中可用。 Office 对象代表插入文档、工作簿、演示文稿、项目、电子邮件或约会安排中的具体应用程序实例。 它可以使用 select 方法访问应用程序和文档之间的绑定。 (我们将在以后的文章中更深入地讨论绑定。) 最重要的是,Office 对象公开应用程序的初始化事件,这使您能够构建应用程序的初始化逻辑(以后的文章中将介绍更多相关内容)。 最后,Office 对象包含对应用程序的 Context 对象的引用。

Context 对象也对所有类型的应用程序并在所有主机应用程序中可用,它公开有关托管应用程序的运行时环境的信息。 除了存储应用程序的语言设置之外,Context 对象还提供 JavaScript API for Office 中的运行时功能的入口点,这些功能特定于激活该应用程序的主机。

例如,可以通过 Context.document 属性访问与应用程序相关联的文档(Document 对象)。 但是,此属性仅当从支持它的主机应用程序中(也就是说从任务窗格或内容应用程序中)调用时才会返回值。 如果尝试从邮件应用程序访问 Context.document 属性,则会出现“未定义的对象”错误。 Context.mailbox 属性也是如此: 在邮件应用程序中,它返回主机应用程序中打开的邮箱(Mailbox 对象)。 在任务窗格应用程序中,该属性未定义。

对任务窗格和内容的支持 对象模型中的应用程序 对于任务窗格和内容应用程序,Document 对象代表插入了应用程序的文档、工作簿、演示文稿或项目。 Document 对象提供对文件内容最高程度的访问,实质上,它是应用程序与 Office 文档之间的主要联系点。

访问 Office 文档内容的所有方法几乎都需要使用 Document 对象。 因此,如图 3 所示,您可能希望在应用程序初始化时捕获对 Document 对象的引用。

图 3 在应用程序初始化时存储对 Document 对象的引用

// Add a handler to the initialize event of the Office object
Office.initialize = function (reason) {
  $(document).ready(function () {
    app.get_Document(Office.context.document);
 
    // Other initialization logic goes here
  })
}
 
// Use a self-executing anonymous function to encapsulate the
// functionality that the app uses
var app = (function () {
 
  var _document;
  function get_Document(officeDocument) {
    _document = officeDocument;
  }
 
  // Other fields and functions associated with the app
 
  return {
    get_Document: get_Document
    // Other exposed members
  };
})()

在 Project 文件中激活一个应用程序时,Document 对象将公开针对 Project 文件的其他特定功能。 通过 Document 对象,应用程序可以获取项目中特定任务、视图、字段和资源的数据。 应用程序还可以添加事件侦听器以监视用户对项目中所选视图、任务或资源的更改。 (在下一篇文章中,我们将更详细地讨论如何在 Project 应用程序中使用 Document 对象。)

Document 对象还公开 Settings 对象,该对象表示应用程序的“属性包”。 使用 Settings 对象,应用程序可以在同一文档中跨不同应用程序会话存储和持久保存自定义属性。 这些属性随文档移动: 如果您与他人共享某个包含应用程序的 Office 文件,则当此人读取该文件时,存储在该应用程序中的自定义属性将可用。

使用属性包存储和检索设置很简单。 Settings.set 方法会在内存中以键/值对形式创建设置。 为从属性包中取出属性,我们要传入设置的名称(键)来使用 Settings.get 方法获取值。 Set 和 Get 方法都是同步方法。 要跨会话存储设置,我们需要调用 Settings.saveAsync 方法,该方法在保存文档时保存包含在应用程序中的所有自定义属性。

示例代码“Office 应用程序: 持久保存自定义设置”(bit.ly/UEiZff) 另外提供了一些示例,演示如何使用 Settings 对象以及如何在应用程序中存储数据。

对象模型中对邮件应用程序的支持 对于邮件应用程序,Mailbox 对象提供了邮件应用程序特定功能的数据访问入口点。 顾名思义,Mailbox 对象对应于当前用户的邮箱,无论用户在何处(无论在 Outlook 客户端应用程序还是在 Outlook Web App 中)阅读他们的电子邮件,该对象都随之移动。 除了提供对各电子邮件和约会安排的访问(通过 Mailbox.item 属性)之外,Mailbox 对象还允许应用程序创建新的约会安排、访问本地用户的配置文件,甚至获取用户的本地时间。

就像 Document 对象之于内容和任务窗格应用程序一样,您可能希望在应用程序初始化时捕获对 Mailbox 对象的引用,如图 4 所示。

图 4 应用程序初始化时在全局变量中存储对 Mailbox 对象的引用

// Add a handler to the initialize event of the Office object
Office.initialize = function (reason) {
  $(document).ready(function () {
    app.get_Mailbox(Office.context.mailbox);
 
    // Other initialization logic goes here
  })
}
 
// Use a self-executing anonymous function to encapsulate the
// functionality that the app uses
var app = (function () {
 
  var _mailbox;
  function get_Mailbox(mailbox) {
    _mailbox = mailbox;
  }
 
  // Other fields and functions associated with the app
 
  return {
    get_Mailbox: get_Mailbox
    // Other exposed members
  };
})()

RoamingSettings 对象(也只在邮件应用程序中可用)类似于以文档为中心的应用程序(任务窗格和内容应用程序)的 Settings 对象。 该对象允许应用程序以名称/值对的形式跨会话持久保存自定义属性。 但是,与在主机 Office 文件中保存自定义属性的 Settings 对象不同,RoamingSettings 对象将自定义设置保存到当前用户的邮箱。 这样,无论用户正在查看什么邮件,或用户如何访问其邮箱(无论在 Outlook 中还是 Outlook Web App 中访问),都能使自定义属性对应用程序可用。

有关 JavaScript API for Office 中对象模型层次结构的更多信息,请参见 MSDN 文档页面“了解 JavaScript API for Office”(bit.ly/UV2POY)。

测试某项功能在主机应用程序中是否可用

如前所述,JavaScript API for Office 的优势之一是 Office 应用程序的“一次开发,多处使用”性质。 例如,同一个任务窗格应用程序在 Word、Excel、Project 和 PowerPoint 中都可激活(假设其清单允许所有这些功能)。

但是,由于并非所有应用程序都可以访问完全相同的一组功能,所以某个应用程序可能被插入一个不允许该应用程序所需功能的主机应用程序中。 例如,Project 目前不提供对 Settings 对象的访问。 如果某个插入 Project 的应用程序尝试访问 Settings 对象,则会引发“未定义的对象”错误。

因此,开发人员必须在其应用程序中包含用于测试他们所需要的功能是否可用的逻辑。 在 Project 的示例中,检测主机应用程序中的功能的最佳方法是使用一个简单的 if 块:

// Test for Settings object in host application
if (Office.context.document.settings) {
 
  // Provide implementation that uses the Settings object
 
}
else {
 
  // Use some other technique for saving custom properties,
  // like localStorage, sessionStorage or cookies
 
}

有关如何检测某成员在主机应用程序中是否可用的更多信息,请参见 MSDN 文档页面“如何: 确定主机应用程序是否支持特定 API 成员”,地址是 bit.ly/TR5ZlB

在本文中,我们讨论了 JavaScript API for Office 的重要概念。 我们概括描述了对象模型层次结构,讨论了异步模式在对象模型中的实现。 我们还介绍了如何测试在主机应用程序中是否支持某个功能。

在本系列的下一篇文章中,我们将进一步讨论在用于 Office 的应用程序中处理数据的最简单而又最强大的方法。 文章将更深入地描述如何获取和设置选定数据。 我们将介绍如何获取所有文件内容以及如何对内容进行解析。 此外,还会讨论 Project 中的应用程序以及如何读取任务、资源和视图数据。 最后,我们将考察 JavaScript API for Office 中的事件模型: 您可以编写代码的事件以及处理结果的方法。

Stephen Oliver*  *是 Office 部门的一位程序员,也是 Microsoft 认证专业开发人员 (SharePoint 2010)。 他针对 Excel 服务和 Word 自动化服务编写开发人员文档,还编写 PowerPoint 自动化服务开发人员文档。 他帮助组织和设计过 Excel Mashup 站点,网址是 ExcelMashup.com

Eric Schmidt*  *是 Office 部门的一位程序员。 他为用于 Office 的应用程序创建了多段示例代码,包括很普及的“持久保存自定义设置”示例代码。 此外,他还就 Office 可编程性撰写过有关其他产品和技术的文章并创建过相关视频。

衷心感谢以下技术专家对本文的审阅: Mark Brewster、Shilpa Kothari 和 Juan Balmori Labra