教程:创建 Excel 任务窗格加载项
本文内容
先决条件
创建加载项项目
创建表
筛选和排序表格
创建图表
冻结表格标题
保护工作表
打开对话框
后续步骤
代码示例
另请参阅
显示另外 7 个
在本教程中,将创建 Excel 任务窗格加载项,该加载项将:
创建表格
筛选和排序表格
创建图表
冻结表格标题
保护工作表
打开对话框
Node.js(最新LTS 版本)。 访问 Node.js 站点 ,下载并安装适合你的操作系统的版本。
最新版本的 Yeoman 和适用于 Office 加载项的 Yeoman 生成器。若要全局安装这些工具,请从命令提示符处运行以下命令。
npm install -g yo generator-office
备注
即便先前已安装了 Yeoman 生成器,我们还是建议你通过 npm 将包更新为最新版本。
已连接到 Microsoft 365 订阅的 Office (包括 Office 网页版)。
运行以下命令,使用 Yeoman 生成器创建加载项项目。 包含项目的文件夹将添加到当前目录。
yo office
备注
运行该yo office
命令时,可能会收到有关 Yeoman 和 Office 加载项 CLI 工具的数据收集策略的提示。 根据你的需要,使用提供的信息来响应提示。
出现提示时,请提供以下信息以创建加载项项目。
选择项目类型: Office Add-in Task Pane project
选择脚本类型: JavaScript
要为外接程序命名什么名称? My Office Add-in
你希望支持哪个 Office 客户端应用程序? Excel
完成此向导后,生成器会创建项目,并安装支持的 Node 组件。 如果在初始设置过程中出现问题,可能需要在项目的根文件夹中手动运行 npm install
。
本教程的这一步是,以编程方式测试加载项是否支持用户的当前版本 Excel,向工作表中添加表格,使用数据填充表格,并设置格式。
在代码编辑器中打开项目。
打开 ./src/taskpane/taskpane.html 文件。 此文件含有任务窗格的 HTML 标记。
找到 <main>
元素并删除在开始 <main>
标记后和关闭 </main>
标记前出现的所有行。
打开 <main>
标记后立即添加下列标记:
<button class ="ms-Button" id ="create-table" > Create Table</button > <br /> <br />
打开 ./src/taskpane/taskpane.js 文件。 此文件包含用于加快任务窗格与 Office 客户端应用程序之间的交互的 Office JavaScript API 代码。
执行以下操作,删除对 run
按钮和 run()
函数的所有引用:
在 Office.onReady
函数调用中,找到行 if (info.host === Office.HostType.Excel) {
并紧跟该行添加下列代码。 注意:
此代码为 create-table
按钮添加事件处理程序。
函数 createTable
包装在调用 tryCatch
中, (将在下一步) 添加这两个函数。 这允许独立于服务代码处理 Office JavaScript 层生成的任何错误。
document .getElementById("create-table" ).onclick = () => tryCatch(createTable);
将以下函数添加到文件末尾。 注意:
Excel.js 业务逻辑将添加到传递给 Excel.run
的函数。 此逻辑不立即执行。 相反,它会被添加到挂起的命令队列中。
context.sync
方法将所有已排入队列的命令发送到 Excel 以供执行。
通过 tryCatch
任务窗格与工作簿交互的所有函数都将使用该函数。 以这种方式捕获 Office JavaScript 错误是一种通用处理任何未捕获的错误的便捷方法。
async function createTable ( ) {
await Excel.run(async (context) => {
await context.sync();
});
}
async function tryCatch (callback ) {
try {
await callback();
} catch (error) {
console .error(error);
}
}
在 createTable()
函数中,将 TODO1
替换为以下代码。 注意:
该代码使用 add
工作表的表集合的 方法创建一个表,即使它为空,该集合也始终存在。 这是创建 Excel.js 对象的标准方式。 没有类构造函数 API,切勿使用 new
运算符创建 Excel 对象。 相反,请添加到父集合对象。
add
方法的第一个参数仅是表格最上面一行的范围,而不是表格最终使用的整个范围。 这是因为当加载项填充数据行时(在下一步中),它将新行添加到表中,而不是将值写入现有行的单元格。 这是一种常见模式,因为在创建表时,表将具有的行数通常未知。
表名称必须在整个工作簿中都是唯一的,而不仅仅是在工作表一级。
const currentWorksheet = context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.add("A1:D1" , true );
expensesTable.name = "ExpensesTable" ;
在 createTable()
函数中,将 TODO2
替换为以下代码。 注意:
expensesTable.getHeaderRowRange().values =
[["Date" , "Merchant" , "Category" , "Amount" ]];
expensesTable.rows.add(null , [
["1/1/2017" , "The Phone Company" , "Communications" , "120" ],
["1/2/2017" , "Northwind Electric Cars" , "Transportation" , "142.33" ],
["1/5/2017" , "Best For You Organics Company" , "Groceries" , "27.9" ],
["1/10/2017" , "Coho Vineyard" , "Restaurant" , "33" ],
["1/11/2017" , "Bellows College" , "Education" , "350.1" ],
["1/15/2017" , "Trey Research" , "Other" , "135" ],
["1/15/2017" , "Best For You Organics Company" , "Groceries" , "97.88" ]
]);
在 createTable()
函数中,将 TODO3
替换为以下代码。 注意:
此代码将从零开始编制的索引传递给表格的列集合的 getItemAt
方法,以获取对“金额” 列的引用。
备注
Excel.js 集合对象(如 TableCollection
、WorksheetCollection
和 TableColumnCollection
)有 items
属性,此属性是子对象类型的数组(如 Table
、Worksheet
或 TableColumn
),但 *Collection
对象本身并不是数组。
然后,此代码将“金额” 列的范围格式化为欧元(精确到小数点后两位)。 在数字格式代码一文中详细了解 Excel 数字格式 语法/
最后,它确保了列宽和行高足以容纳最长(或最高)的数据项。 请注意,此代码必须获取要格式化的 Range
对象。
TableColumn
和 TableRow
对象没有格式属性。
expensesTable.columns.getItemAt(3 ).getRange().numberFormat = [['\u20AC#,##0.00' ]];
expensesTable.getRange().format.autofitColumns();
expensesTable.getRange().format.autofitRows();
验证是否已保存了对项目所做的所有更改。
完成以下步骤,以启动本地 Web 服务器并旁加载你的加载项。
备注
即使在开发过程中,Office 外接程序也应使用 HTTPS,而不是 HTTP。 如果在运行以下命令之一后系统提示安装证书,请接受安装 Yeoman 生成器提供的证书的提示。 你可能还必须以管理员身份运行命令提示符或终端才能进行更改。
如果这是你第一次在计算机上开发 Office 加载项,则命令行中可能会提示你授予Microsoft Edge WebView 环回豁免 (“允许 Microsoft Edge WebView 的 localhost 环回?”) 。 出现提示时,输入 Y
以允许豁免。 请注意,需要管理员权限才能允许豁免。 一旦允许,在将来 (旁加载 Office 加载项时,系统就不会提示你获得豁免,除非从计算机) 中删除该豁免。 若要了解详细信息,请参阅加载 Office 外接程序或使用 Fiddler 时,“我们无法从 localhost 打开此外接程序”。
提示
如果在 Mac 上测试加载项,请先运行项目根目录中的以下命令,然后再继续。 运行此命令时,本地 Web 服务器将启动。
npm run dev-server
若要在 Excel 中测试加载项,请在项目的根目录中运行以下命令。 这将启动本地的 Web 服务器 (如果尚未运行的话), 并使用加载的加载项打开 Excel。
npm start
若要在 Excel 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将 "{url}" 替换为你拥有权限的 OneDrive 或 SharePoint 库上 Excel 文档的 URL。
备注
如果在 Mac 上进行开发,请将 括 {url}
在单引号中。
请勿 在 Windows 上执行此操作。
npm run start -- web --document {url}
示例如下。
npm run start -- web --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start -- web --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start -- web --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版 。
在 Excel 中,选择“ 开始 ”选项卡,然后选择功能区上的“ 显示任务窗格 ”按钮以打开加载项任务窗格。
在任务窗格中,选择“创建表 ”按钮。
如果要停止本地 Web 服务器并卸载加载项,请按照适用的说明操作:
本教程的这一步是,筛选并排序之前创建的表。
打开 ./src/taskpane/taskpane.html 文件。
查找create-table
按钮的<button>
元素,并在行后添加下列标记。
<button class ="ms-Button" id ="filter-table" > Filter Table</button > <br /> <br />
打开 ./src/taskpane/taskpane.js 文件。
在 Office.onReady
函数调用中,定位将单击处理程序分配到 create-table
按钮的行,并在该行后添加以下代码。
document .getElementById("filter-table" ).onclick = () => tryCatch(filterTable);
将以下函数添加到文件结尾。
async function filterTable ( ) {
await Excel.run(async (context) => {
await context.sync();
});
}
在 filterTable()
函数中,将 TODO1
替换为以下代码。 注意:
代码先将列名称传递给 getItem
方法(而不是像 getItemAt
方法一样将列索引传递给 createTable
方法),获取对需要筛选的列的引用。 由于用户可以移动表格列,因此给定索引处的列可能会在表格创建后更改。 所以,更安全的做法是,使用列名称获取对列的引用。 上一教程安全地使用了 getItemAt
,因为是在与创建表格完全相同的方法中使用了它,所以用户没有机会移动列。
applyValuesFilter
方法是对 Filter
对象执行的多种筛选方法之一。
const currentWorksheet = context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.getItem('ExpensesTable' );
const categoryFilter = expensesTable.columns.getItem('Category' ).filter;
categoryFilter.applyValuesFilter(['Education' , 'Groceries' ]);
打开 ./src/taskpane/taskpane.html 文件。
查找filter-table
按钮的<button>
元素,并在行后添加下列标记。
<button class ="ms-Button" id ="sort-table" > Sort Table</button > <br /> <br />
打开 ./src/taskpane/taskpane.js 文件。
在 Office.onReady
函数调用中,定位将单击处理程序分配到 filter-table
按钮的行,并在该行后添加以下代码。
document .getElementById("sort-table" ).onclick = () => tryCatch(sortTable);
将以下函数添加到文件结尾。
async function sortTable ( ) {
await Excel.run(async (context) => {
await context.sync();
});
}
在 sortTable()
函数中,将 TODO1
替换为以下代码。 注意:
此代码创建一组 SortField
对象,其中只有一个成员,因为加载项只对“商家”列进行了排序。
SortField
对象的 key
属性是用于排序的列的从零开始编制索引。 表中的行按照所引用列中的值进行排序。
Table
的 sort
成员是 TableSort
对象,并不是方法。
SortField
传递到 TableSort
对象的 apply
方法。
const currentWorksheet = context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.getItem('ExpensesTable' );
const sortFields = [
{
key : 1 ,
ascending: false ,
}
];
expensesTable.sort.apply(sortFields);
验证是否已保存了对项目所做的所有更改。
如果本地 Web 服务器已在运行,并且加载项已加载到 Excel 中,请继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项:
若要在 Excel 中测试加载项,请在项目的根目录中运行以下命令。 这将启动本地的 Web 服务器 (如果尚未运行的话), 并使用加载的加载项打开 Excel。
npm start
若要在 Excel 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将 "{url}" 替换为你拥有权限的 OneDrive 或 SharePoint 库上 Excel 文档的 URL。
备注
如果在 Mac 上进行开发,请将 括 {url}
在单引号中。
请勿 在 Windows 上执行此操作。
npm run start -- web --document {url}
示例如下。
npm run start -- web --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start -- web --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start -- web --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版 。
如果加载项任务窗格尚未在 Excel 中打开,请转到“ 开始 ”选项卡,然后选择功能区上的“ 显示任务窗格 ”按钮将其打开。
如果之前在此教程中添加的表格未在打开的工作表中出现,选择任务窗格中的“创建表 ”按钮。
选择“筛选表 ”按钮和“排序表 ”按钮(按顺序和倒序中的任一顺序排序皆可)。
本教程的这一步是,使用先前创建的表中的数据创建图表,再设置图表格式。
打开 ./src/taskpane/taskpane.html 文件。
查找sort-table
按钮的<button>
元素,并在行后添加下列标记。
<button class ="ms-Button" id ="create-chart" > Create Chart</button > <br /> <br />
打开 ./src/taskpane/taskpane.js 文件。
在 Office.onReady
函数调用中,定位将单击处理程序分配到 sort-table
按钮的行,并在该行后添加以下代码。
document .getElementById("create-chart" ).onclick = () => tryCatch(createChart);
将以下函数添加到文件结尾。
async function createChart ( ) {
await Excel.run(async (context) => {
await context.sync();
});
}
在 createChart()
函数中,将 TODO1
替换为以下代码。 请注意,为了排除标题行,此代码使用 Table.getDataBodyRange
方法(而不是 getRange
方法),获取要绘制成图表的数据的范围。
const currentWorksheet = context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.getItem('ExpensesTable' );
const dataRange = expensesTable.getDataBodyRange();
在 createChart()
函数中,将 TODO2
替换为以下代码。 请注意以下参数。
const chart = currentWorksheet.charts.add('ColumnClustered' , dataRange, 'Auto' );
在 createChart()
函数中,将 TODO3
替换为以下代码。 此代码的大部分内容非常直观明了。 请注意几下几点:
chart.setPosition("A15" , "F30" );
chart.title.text = "Expenses" ;
chart.legend.position = "Right" ;
chart.legend.format.fill.setSolidColor("white" );
chart.dataLabels.format.font.size = 15 ;
chart.dataLabels.format.font.color = "black" ;
chart.series.getItemAt(0 ).name = 'Value in \u20AC' ;
验证是否已保存了对项目所做的所有更改。
如果本地 Web 服务器已在运行,并且加载项已加载到 Excel 中,请继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项:
若要在 Excel 中测试加载项,请在项目的根目录中运行以下命令。 这将启动本地的 Web 服务器 (如果尚未运行的话), 并使用加载的加载项打开 Excel。
npm start
若要在 Excel 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将 "{url}" 替换为你拥有权限的 OneDrive 或 SharePoint 库上 Excel 文档的 URL。
备注
如果在 Mac 上进行开发,请将 括 {url}
在单引号中。
请勿 在 Windows 上执行此操作。
npm run start -- web --document {url}
示例如下。
npm run start -- web --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start -- web --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start -- web --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版 。
如果加载项任务窗格尚未在 Excel 中打开,请转到“ 开始 ”选项卡,然后选择功能区上的“ 显示任务窗格 ”按钮将其打开。
如果之前在此教程中添加的表格未在打开的工作表中出现,选择任务窗格中的“创建表 ”按钮,随后选择“筛选表 ”按钮和“排序表 ”按钮(按顺序和倒序中的任一顺序排序皆可)。
选择“创建图表”**** 按钮。 此时,图表创建完成,其中仅包含筛选出的行中的数据。 底部数据点上的标签按图表的排序顺序进行排序,即按商家名称的字母倒序排序。
如果表格很长,导致用户必须滚动才能看到一些行,那么标题行可能会在滚动时不可见。 本教程的这一步是,冻结以前创建的表格的标题行,让它在用户向下滚动工作表时依然可见。
打开 ./src/taskpane/taskpane.html 文件。
查找create-chart
按钮的<button>
元素,并在行后添加下列标记。
<button class ="ms-Button" id ="freeze-header" > Freeze Header</button > <br /> <br />
打开 ./src/taskpane/taskpane.js 文件。
在 Office.onReady
函数调用中,定位将单击处理程序分配到 create-chart
按钮的行,并在该行后添加以下代码。
document .getElementById("freeze-header" ).onclick = () => tryCatch(freezeHeader);
将以下函数添加到文件结尾。
async function freezeHeader ( ) {
await Excel.run(async (context) => {
await context.sync();
});
}
在 freezeHeader()
函数中,将 TODO1
替换为以下代码。 注意:
const currentWorksheet = context.workbook.worksheets.getActiveWorksheet();
currentWorksheet.freezePanes.freezeRows(1 );
验证是否已保存了对项目所做的所有更改。
如果本地 Web 服务器已在运行,并且加载项已加载到 Excel 中,请继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项:
若要在 Excel 中测试加载项,请在项目的根目录中运行以下命令。 这将启动本地的 Web 服务器 (如果尚未运行的话), 并使用加载的加载项打开 Excel。
npm start
若要在 Excel 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将 "{url}" 替换为你拥有权限的 OneDrive 或 SharePoint 库上 Excel 文档的 URL。
备注
如果在 Mac 上进行开发,请将 括 {url}
在单引号中。
请勿 在 Windows 上执行此操作。
npm run start -- web --document {url}
示例如下。
npm run start -- web --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start -- web --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start -- web --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版 。
如果加载项任务窗格尚未在 Excel 中打开,请转到“ 开始 ”选项卡,然后选择功能区上的“ 显示任务窗格 ”按钮将其打开。
如果之前在此教程中添加的表格出现在此工作表中,将其删除。
在任务窗格中,选择“创建表 ”按钮。
在任务窗格中,选择“冻结标题 ”按钮。
尽量向下滚动工作表,直到在上面的行不可见时表格标题在顶部依然可见。
在此教程的此步骤中,将向功能区添加一个按钮,以打开和关闭工作表保护。
打开清单文件 ./manifest.xml 。
找到 <Control> 元素。 此元素定义了“主页” 功能区上一直用于启动加载项的“显示任务窗格” 按钮。 将向“主页” 功能区上的相同组添加第二个按钮。 在结束 </Control> 标记和结束 </Group> 标记之间,添加以下标记。
<Control xsi:type ="Button" id ="<!--TODO1: Unique (in manifest) name for button -->" >
<Label resid ="<!--TODO2: Button label -->" />
<Supertip >
<Title resid ="<!-- TODO3: Button tool tip title -->" />
<Description resid ="<!-- TODO4: Button tool tip description -->" />
</Supertip >
<Icon >
<bt:Image size ="16" resid ="Icon.16x16" />
<bt:Image size ="32" resid ="Icon.32x32" />
<bt:Image size ="80" resid ="Icon.80x80" />
</Icon >
<Action xsi:type ="<!-- TODO5: Specify the type of action-->" >
</Action >
</Control >
在刚添加至清单文件的 XML 内,将 TODO1
替换为字符串,以便向按钮提供在此清单文件内唯一的 ID。 由于按钮将启用和禁用工作表保护,因此请使用“ToggleProtection”。 完成时,Control
元素的开始标记如下所示:
<Control xsi:type ="Button" id ="ToggleProtection" >
接下来的三个 TODO
设置资源 ID,或 resid
。 资源是字符串(最大长度为 32 个字符),这三个字符串将在后续步骤中创建。 现在,需要向资源提供 ID。 虽然按钮标签应名为“切换保护”,但此字符串的 ID 应为“ProtectionButtonLabel”。因此 Label
元素的样式如下:
<Label resid ="ProtectionButtonLabel" />
SuperTip
元素定义了按钮的工具提示。 由于工具提示标题应与按钮标签相同,因此使用完全相同的资源 ID,即“ProtectionButtonLabel”。 工具提示说明为“单击即可启用和禁用工作表保护”。 不过,resid
应为“ProtectionButtonToolTip”。 完成后,SuperTip
元素如下所示:
<Supertip >
<Title resid ="ProtectionButtonLabel" />
<Description resid ="ProtectionButtonToolTip" />
</Supertip >
备注
在生产加载项中,不建议对两个不同的按钮使用相同的图标;但为了简单起见,本教程将采用这样的做法。 因此,新 Icon
中的 Control
标记直接就是现有 Icon
中 Control
元素的副本。
虽然原始 Control
元素内的 Action
元素的类型设置为 ShowTaskpane
,但新按钮不会要打开任务窗格,而是要运行在后续步骤中创建的自定义函数。 因此,将 TODO5
替换为 ExecuteFunction
,即触发自定义函数的按钮的操作类型。
Action
元素的开始标记如下所示:
<Action xsi:type ="ExecuteFunction" >
原始 Action
元素的子元素指定任务窗格 ID,以及应当在任务窗格中打开的页面 URL。 不过,Action
类型的 ExecuteFunction
元素只有一个子元素,用于命名控件执行的函数。 此函数(名为 toggleProtection
)将在后续步骤中创建。 因此,请将 替换为 TODO6
以下标记。
<FunctionName > toggleProtection</FunctionName >
此时,整个 Control
标记应如下所示:
<Control xsi:type ="Button" id ="ToggleProtection" >
<Label resid ="ProtectionButtonLabel" />
<Supertip >
<Title resid ="ProtectionButtonLabel" />
<Description resid ="ProtectionButtonToolTip" />
</Supertip >
<Icon >
<bt:Image size ="16" resid ="Icon.16x16" />
<bt:Image size ="32" resid ="Icon.32x32" />
<bt:Image size ="80" resid ="Icon.80x80" />
</Icon >
<Action xsi:type ="ExecuteFunction" >
<FunctionName > toggleProtection</FunctionName >
</Action >
</Control >
向下滚动到清单的 Resources
部分。
将下列标记添加为 bt:ShortStrings
元素的子级。
<bt:String id ="ProtectionButtonLabel" DefaultValue ="Toggle Worksheet Protection" />
将下列标记添加为 bt:LongStrings
元素的子级。
<bt:String id ="ProtectionButtonToolTip" DefaultValue ="Click to protect or unprotect the current worksheet." />
保存文件。
打开文件 .\commands\commands.js 。
紧接着 action
函数添加下列函数。 注意,我们向函数和函数调用 args.completed
的最后一行指定了 args
参数。
ExecuteFunction 类型的所有加载项命令都必须满足这项要求。 它会指示 Office 客户端应用程序,函数已完成,且 UI 可以再次变得可响应。
async function toggleProtection (args ) {
try {
await Excel.run(async (context) => {
await context.sync();
});
} catch (error) {
console .error(error);
}
args.completed();
}
紧跟在 函数后面添加以下行以将其注册。
Office.actions.associate("toggleProtection" , toggleProtection);
在 toggleProtection
函数中,将 TODO1
替换为以下代码。 此代码使用处于标准切换模式的工作表对象 protection 属性。
TODO2
将在下一部分中进行介绍。
const sheet = context.workbook.worksheets.getActiveWorksheet();
if (sheet.protection.protected) {
sheet.protection.unprotect();
} else {
sheet.protection.protect();
}
在此教程中创建的各函数内,通过对命令进行排队来写入 Office 文档。 每个函数结束时都会调用 context.sync()
方法,从而将排入队列的命令发送到文档,以供执行。 但是在上一步中添加的代码调用的是 sheet.protection.protected property
, 这与之前编写的函数明显不同,因为 sheet
对象只是任务窗格脚本中的代理对象。 代理对象并不了解文档的实际保护状态,因此它的 protection.protected
属性无法有实值。 为避免出现异常错误,必须首先从文档中获取保护状态并使用它来设置值 sheet.protection.protected
。 此获取过程分为三步。
将命令排入队列,以加载(即提取)代码需要读取的属性。
调用上下文对象的 sync
方法,从而向文档发送已排入队列的命令以供执行,并返回请求获取的信息。
由于 sync
是异步方法,因此请先确保它已完成,然后代码才能调用已提取的属性。
只要代码需要从 Office 文档读取 信息,就必须完成这些步骤。
在 toggleProtection
函数中,将 TODO2
替换为以下代码。 注意:
每个 Excel 对象都有 load
方法。 对于要在参数中读取的对象属性,将它们指定为逗号分隔名称字符串。 在此示例中,需要读取的属性为 protection
属性的子属性。 引用子属性的方法与在代码中的其他任何地方引用属性几乎完全一样,不同之处在于使用的是正斜杠(“/”)字符,而不是“.”字符。
要确保切换逻辑(显示为 sheet.protection.protected
)在 sync
完成且已为 sheet.protection.protected
分配从文档中提取的正确值之后才会运行,它必须在 await
运算符确保 sync
完成后运行。
sheet.load('protection/protected' );
await context.sync();
完成后,整个函数应如下所示:
async function toggleProtection (args ) {
try {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.load('protection/protected' );
await context.sync();
if (sheet.protection.protected) {
sheet.protection.unprotect();
} else {
sheet.protection.protect();
}
await context.sync();
});
} catch (error) {
console .error(error);
}
args.completed();
}
验证是否已保存了对项目所做的所有更改。
关闭所有 Office 应用程序(包括 Excel ()或关闭浏览器选项卡(如果使用Excel web 版) )。
清除 Office 缓存。 这对于从客户端应用程序中完全清除旧版本的外接程序是必需的。
清除 Office 缓存 一文中提供了此过程的说明。
如果本地 Web 服务器已在运行,请在命令提示符下输入以下命令来停止它。 这应关闭节点命令窗口。
npm stop
因为清单文件已更新,必须使用已更新的清单文件,重新旁加载加载项。 启动本地 Web 服务器并旁加载你的加载项。
若要在 Excel 中测试加载项,请在项目的根目录中运行以下命令。 这将启动本地的 Web 服务器 (如果尚未运行的话), 并使用加载的加载项打开 Excel。
npm start
若要在 Excel 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将 "{url}" 替换为你拥有权限的 OneDrive 或 SharePoint 库上 Excel 文档的 URL。
备注
如果在 Mac 上进行开发,请将 括 {url}
在单引号中。
请勿 在 Windows 上执行此操作。
npm run start -- web --document {url}
示例如下。
npm run start -- web --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start -- web --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start -- web --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版 。
在 Excel 的 Home 选项卡上,选择“切换工作表保护 ”按钮。 请注意,功能区上的大部分控件都处于禁用状态(灰显),如下面的屏幕截图所示。
选择单元格并尝试编辑其内容。 Excel 将显示一条错误消息,指示工作表受保护。
再次选择“切换工作表保护 ”按钮,此时控件重新启用,可以再次更改单元格值。
本教程的最后一步是,在加载项中打开对话框,将消息从对话框进程传递到任务窗格进程,再关闭对话框。 Office 外接程序对话框是 非模式的 :用户可以继续与 Office 应用程序中的文档和任务窗格中的主机页面进行交互。
在项目根目录下的 ./src 文件夹中,新建文件夹“dialogs ”。
在 ./src/dialogs 文件夹中,新建文件“popup.html ”。
将下面的标记添加到 popup.html 中。 注意:
此页面包含可供用户输入用户名的 <input>
字段,并包含将此名称发送到将在其中显示的任务窗格的按钮。
此标记加载在后续步骤中创建的 popup.js 脚本。
此标记还加载 Office.JS 库,因为 popup.js 将使用它们。
<!DOCTYPE html>
<html >
<head lang ="en" >
<title > Dialog for My Office Add-in</title >
<meta charset ="UTF-8" >
<meta name ="viewport" content ="width=device-width, initial-scale=1" >
<link rel ="stylesheet" href ="https://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-core/11.0.0/css/fabric.min.css" />
<script type ="text/javascript" src ="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" > </script >
<script type ="text/javascript" src ="popup.js" > </script >
</head >
<body style ="display:flex;flex-direction:column;align-items:center;justify-content:center" >
<p class ="ms-font-xl" > ENTER YOUR NAME</p >
<input id ="name-box" type ="text" /> <br /> <br />
<button id ="ok-button" class ="ms-Button" > OK</button >
</body >
</html >
在 ./src/dialogs 文件夹中,新建文件“popup.js ”。
将下面的代码添加到 popup.js 中。 对于此代码,请注意以下事项。
每个调用 Office.JS 库中的 API 的页面均必须首先确保该库已完成初始化。 执行此操作的最佳方法是调用 Office.onReady()
函数。 必须在调用 Office.js 之前运行 Office.onReady()
调用;因此,作业位于由页面加载的脚本文件中,如同本示例中一样。
Office.onReady((info ) => {
});
将 TODO1
替换为下面的代码。 将在下一步中创建 sendStringToParentPage
函数。
document .getElementById("ok-button" ).onclick = () => tryCatch(sendStringToParentPage);
将 TODO2
替换为以下代码。
messageParent
方法将它的参数传递到父页面(在此示例中,为任务窗格中的页面)。 参数必须是字符串,其中包括任何可以序列化为字符串的内容(例如 XML 或 JSON),或者任何可以转换为字符串的类型。 这还会添加 taskpane.js 中使用的相同tryCatch
方法,用于错误处理。
function sendStringToParentPage ( ) {
const userName = document .getElementById("name-box" ).value;
Office.context.ui.messageParent(userName);
}
async function tryCatch (callback ) {
try {
await callback();
} catch (error) {
console .error(error);
}
}
备注
popup.html 文件及其加载的 popup.js 文件在完全独立于加载项任务窗格的浏览器进程中运行。 如果将 popup.js 转换为与 app.js 文件相同的 bundle.js 文件,加载项必须加载 bundle.js 文件的两个副本,这就违背了绑定目的。 所以此加载项根本不会转换 popup.js 。
打开项目根目录中的 webpack.config.js 文件,并完成以下步骤。
在 config
对象内找到 entry
对象并为 popup
添加新条目。
popup: "./src/dialogs/popup.js"
完成此操作之后,新的 entry
对象将与此类似。
entry: {
polyfill : "@babel/polyfill" ,
taskpane : "./src/taskpane/taskpane.js" ,
commands : "./src/commands/commands.js" ,
popup : "./src/dialogs/popup.js"
},
在 config
对象中找到 plugins
数组,并添加下列对象至数组的结尾。
new HtmlWebpackPlugin({
filename : "popup.html" ,
template : "./src/dialogs/popup.html" ,
chunks : ["polyfill" , "popup" ]
})
完成此操作之后,新的 plugins
数组将与此类似。
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename : "taskpane.html" ,
template : "./src/taskpane/taskpane.html" ,
chunks : ['polyfill' , 'taskpane' ]
}),
new CopyWebpackPlugin([
{
to : "taskpane.css" ,
from : "./src/taskpane/taskpane.css"
}
]),
new HtmlWebpackPlugin({
filename : "commands.html" ,
template : "./src/commands/commands.html" ,
chunks : ["polyfill" , "commands" ]
}),
new HtmlWebpackPlugin({
filename : "popup.html" ,
template : "./src/dialogs/popup.html" ,
chunks : ["polyfill" , "popup" ]
})
],
如果本地 Web 服务器正在运行,请在命令提示符下输入以下命令来停止它。 这应关闭节点命令窗口。
npm stop
运行以下命令以重建项目。
npm run build
打开 ./src/taskpane/taskpane.html 文件。
查找freeze-header
按钮的<button>
元素,并在行后添加下列标记。
<button class ="ms-Button" id ="open-dialog" > Open Dialog</button > <br /> <br />
对话框会提示用户输入用户名,并将用户名传递到任务窗格。 任务窗格将在标签中显示用户名。 紧接着刚添加的 button
,添加下列标记。
<label id ="user-name" > </label > <br /> <br />
打开 ./src/taskpane/taskpane.js 文件。
在 Office.onReady
函数调用中,定位将单击处理程序分配到 freeze-header
按钮的行,并在该行后添加以下代码。 将在后续步骤中创建 openDialog
方法。
document .getElementById("open-dialog" ).onclick = openDialog;
添加下列声明至文件结尾。 此变量用于保留父页面执行上下文中的对象,以用作对话框页面执行上下文的中间对象。
let dialog = null ;
添加下列函数至文件结尾(dialog
声明后)。 关于此代码,请务必注意它不 包含的内容,即不含 Excel.run
调用。 这是因为用于打开对话框的 API 跨所有 Office 应用程序共享,所以它属于 Office JavaScript 通用 API,而非 Excel 专用 API。
function openDialog ( ) {
}
将 TODO1
替换为下面的代码。 注意:
Office.context.ui.displayDialogAsync(
'https://localhost:3000/popup.html' ,
{height : 45 , width : 55 },
);
在 ./src/taskpane/taskpane.js 文件的 openDialog
函数内,将 TODO2
替换为下列代码。 注意:
回调在对话框成功打开后和用户在对话框中执行任何操作前立即执行。
result.value
对象用作父页面执行上下文和对话框页面执行上下文的中介。
processMessage
函数将在后续步骤中创建。 此处理程序将处理通过 messageParent
函数调用从对话框页面发送的任何值。
function (result ) {
dialog = result.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
}
在 openDialog
函数后面添加以下函数。
function processMessage (arg ) {
document .getElementById("user-name" ).innerHTML = arg.message;
dialog.close();
}
验证是否已保存了对项目所做的所有更改。
如果本地 Web 服务器已在运行,并且加载项已加载到 Excel 中,请继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项:
若要在 Excel 中测试加载项,请在项目的根目录中运行以下命令。 这将启动本地的 Web 服务器 (如果尚未运行的话), 并使用加载的加载项打开 Excel。
npm start
若要在 Excel 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将 "{url}" 替换为你拥有权限的 OneDrive 或 SharePoint 库上 Excel 文档的 URL。
备注
如果在 Mac 上进行开发,请将 括 {url}
在单引号中。
请勿 在 Windows 上执行此操作。
npm run start -- web --document {url}
示例如下。
npm run start -- web --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start -- web --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start -- web --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版 。
如果加载项任务窗格尚未在 Excel 中打开,请转到“ 开始 ”选项卡,然后选择功能区上的“ 显示任务窗格 ”按钮将其打开。
选择任务窗格中的“打开对话框” 按钮。
对话框打开后,拖动它并重设大小。 请注意,你可以与工作表进行交互并按任务窗格上的其他按钮,但无法从同一任务窗格页面启动第二个对话框。
在对话框中,输入名称并选择“确定 ”。 此时,用户名显示在任务窗格上,且对话框关闭。
(可选)在 ./src/taskpane/taskpane.js 文件中,注释掉 函数中的processMessage
行dialog.close();
。 然后,重复执行此部分的步骤。 这样一来,对话框便会继续处于打开状态,可供用户更改用户名。 按右上角的“X” 按钮,可手动关闭对话框。
在本教程中,你已创建与 Excel 工作簿中的表格、图表、工作表和对话框进行交互的 Excel 任务窗格加载项。 若要了解有关构建 Excel 加载项的详细信息,请继续阅读以下文章。