google.script.run
是一个异步客户端 JavaScript API,可让 HTML 服务页面调用服务器端 Apps Script 函数。以下示例展示了 google.script.run
最基本的功能:从客户端 JavaScript 调用服务器上的函数。
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function doSomething() { Logger.log('I was called!'); }
Index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> google.script.run.doSomething(); </script> </head> </html>
如果您将此脚本部署为 Web 应用并访问其网址,则不会看到任何内容,但如果您查看日志,则会看到系统调用了服务器函数 doSomething()
。
客户端对服务器端函数的调用是异步的:在浏览器请求服务器运行函数 doSomething()
后,浏览器会立即继续执行下一行代码,而不会等待响应。这意味着服务器函数调用可能不会按您预期的顺序执行。如果您同时进行两次函数调用,则无法知道哪个函数会先运行;每次加载网页时,结果都可能不同。在这种情况下,成功处理脚本和失败处理脚本有助于控制代码流程。
google.script.run
API 允许对服务器函数进行 10 次并发调用。如果您在 10 个调用仍在运行时发出了第 11 个调用,服务器函数将被延迟,直到 10 个空闲位置之一被释放。实际上,您很少需要考虑此限制,尤其是因为大多数浏览器已将对同一服务器的并发请求数限制在 10 以下。例如,在 Firefox 中,此限制为 6。大多数浏览器也会同样推迟多余的服务器请求,直到某个现有请求完成为止。
参数和返回值
您可以使用客户端中的参数调用服务器函数。同样,服务器函数可以将值作为参数返回给客户端,以便传递给成功处理脚本。
合法参数和返回值采用 Number
、Boolean
、String
或 null
等 JavaScript 原语,以及由原语、对象和数组组成的 JavaScript 对象和数组。网页中的 form
元素也可以用作参数,但它必须是函数的唯一参数,并且不能用作返回值。如果您尝试传递 form
以外的 Date
、Function
、DOM 元素或其他禁止类型(包括对象或数组中的禁止类型),请求将失败。创建循环引用的对象也会失败,并且数组中的未定义字段会变为 null
。
请注意,传递给服务器的对象会成为原始对象的副本。如果服务器函数收到某个对象并更改其属性,客户端上的属性不会受到影响。
成功处理脚本
由于客户端代码会继续执行下一行代码,而不会等待服务器调用完成,因此 withSuccessHandler(function)
允许您指定在服务器响应时要运行的客户端回调函数。如果服务器函数返回值,API 会将该值作为参数传递给新函数。
以下示例会在服务器响应时显示浏览器提醒。请注意,此代码示例需要授权,因为服务器端函数会访问您的 Gmail 账号。授权脚本的最简单方法是在加载页面之前从脚本编辑器手动运行 getUnreadEmails()
函数一次。或者,在部署 Web 应用时,您可以选择以“访问 Web 应用的用户”的身份执行该应用,在这种情况下,系统会在您加载应用时提示您进行授权。
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getUnreadEmails() { return GmailApp.getInboxUnreadCount(); }
Index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function onSuccess(numUnread) { var div = document.getElementById('output'); div.innerHTML = 'You have ' + numUnread + ' unread messages in your Gmail inbox.'; } google.script.run.withSuccessHandler(onSuccess) .getUnreadEmails(); </script> </head> <body> <div id="output"></div> </body> </html>
失败处理程序
如果服务器未响应或抛出错误,withFailureHandler(function)
允许您指定失败处理程序(而非成功处理程序),并将 Error
对象(如果有)作为参数传递。
默认情况下,如果您未指定失败处理脚本,系统会将失败记录到 JavaScript 控制台。如需替换此行为,请调用 withFailureHandler(null)
或提供一个不执行任何操作的失败处理程序。
失败处理脚本的语法与成功处理脚本几乎完全相同,如以下示例所示。
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getUnreadEmails() { // 'got' instead of 'get' will throw an error. return GmailApp.gotInboxUnreadCount(); }
Index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function onFailure(error) { var div = document.getElementById('output'); div.innerHTML = "ERROR: " + error.message; } google.script.run.withFailureHandler(onFailure) .getUnreadEmails(); </script> </head> <body> <div id="output"></div> </body> </html>
User 对象
您可以通过调用 withUserObject(object)
来指定将作为第二个参数传递给处理程序的对象,从而为对服务器的多次调用重复使用相同的成功或失败处理程序。借助此“用户对象”(请勿与 User
类混淆),您可以响应客户端与服务器联系的上下文。由于用户对象不会发送到服务器,因此它们可以是几乎任何内容,包括函数、DOM 元素等,而无需对服务器调用的参数和返回值施加限制。不过,用户对象不能是使用 new
运算符构造的对象。
在此示例中,点击这两个按钮中的任意一个都会使用来自服务器的值更新该按钮,而另一个按钮保持不变,即使它们共用一个成功处理脚本也是如此。在 onclick
处理程序中,关键字 this
是指 button
本身。
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getEmail() { return Session.getActiveUser().getEmail(); }
Index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function updateButton(email, button) { button.value = 'Clicked by ' + email; } </script> </head> <body> <input type="button" value="Not Clicked" onclick="google.script.run .withSuccessHandler(updateButton) .withUserObject(this) .getEmail()" /> <input type="button" value="Not Clicked" onclick="google.script.run .withSuccessHandler(updateButton) .withUserObject(this) .getEmail()" /> </body> </html>
表单
如果您使用 form
元素作为参数调用服务器函数,表单将变为一个对象,其中字段名称为键,字段值为值。除文件输入字段的内容(会转换为 Blob
对象)外,所有值都会转换为字符串。
此示例会处理表单(包括文件输入字段),而无需重新加载网页;它会将文件上传到 Google 云端硬盘,然后在客户端网页中输出文件的网址。在 onsubmit
处理脚本中,关键字 this
是指表单本身。请注意,页面加载后,其中的所有表单的默认提交操作均已由 preventFormSubmit
停用。这样可以防止在出现异常时页面重定向到不准确的网址。
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function processForm(formObject) { var formBlob = formObject.myFile; var driveFile = DriveApp.createFile(formBlob); return driveFile.getUrl(); }
Index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> // Prevent forms from submitting. function preventFormSubmit() { var forms = document.querySelectorAll('form'); for (var i = 0; i < forms.length; i++) { forms[i].addEventListener('submit', function(event) { event.preventDefault(); }); } } window.addEventListener('load', preventFormSubmit); function handleFormSubmit(formObject) { google.script.run.withSuccessHandler(updateUrl).processForm(formObject); } function updateUrl(url) { var div = document.getElementById('output'); div.innerHTML = '<a href="' + url + '">Got it!</a>'; } </script> </head> <body> <form id="myForm" onsubmit="handleFormSubmit(this)"> <input name="myFile" type="file" /> <input type="submit" value="Submit" /> </form> <div id="output"></div> </body> </html>
脚本运行程序
您可以将 google.script.run
视为“脚本运行程序”的构建器。如果您向脚本运行程序添加成功处理脚本、失败处理脚本或用户对象,则不会更改现有运行程序;而是会返回具有新行为的新脚本运行程序。
您可以使用 withSuccessHandler()
、withFailureHandler()
和 withUserObject()
的任意组合和任意顺序。您还可以对已设置值的脚本运行程序调用任何修改函数。新值会直接替换之前的值。
此示例为所有三次服务器调用设置了常见的失败处理脚本,但设置了两个单独的成功处理脚本:
var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);
myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();
私有函数
名称以下划线结尾的服务器函数被视为不公开函数。google.script
无法调用这些函数,并且其名称绝不会发送给客户端。因此,您可以使用它们隐藏需要在服务器上保密的实现细节。google.script
也无法看到库中的函数以及未在脚本的顶级声明的函数。
在此示例中,函数 getBankBalance()
在客户端代码中可用;即使您未调用该函数,检查源代码的用户也能发现其名称。不过,函数 deepSecret_()
和 obj.objectMethod()
对客户端完全不可见。
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getBankBalance() { var email = Session.getActiveUser().getEmail() return deepSecret_(email); } function deepSecret_(email) { // Do some secret calculations return email + ' has $1,000,000 in the bank.'; } var obj = { objectMethod: function() { // More secret calculations } };
Index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function onSuccess(balance) { var div = document.getElementById('output'); div.innerHTML = balance; } google.script.run.withSuccessHandler(onSuccess) .getBankBalance(); </script> </head> <body> <div id="output">No result yet...</div> </body> </html>
调整 Google Workspace 应用中的对话框大小
您可以通过在客户端代码中调用 google.script.host
方法 setWidth(width)
或 setHeight(height)
来调整 Google 文档、表格或表单中的自定义对话框的大小。(如需设置对话框的初始大小,请使用 HtmlOutput
方法 setWidth(width)
和 setHeight(height)
。)请注意,对话框在调整大小后不会在父窗口中重新居中,并且无法调整边栏的大小。
在 Google Workspace中关闭对话框和边栏
如果您使用 HTML 服务在 Google 文档、表格或表单中显示对话框或边栏,则无法通过调用 window.close()
关闭界面。而是必须调用 google.script.host.close()
。如需查看示例,请参阅将 HTML 作为 Google Workspace 界面提供部分。
在 Google Workspace中移动浏览器焦点
如需在用户的浏览器中将焦点从对话框或边栏切换回 Google 文档、表格或表单编辑器,只需调用 google.script.host.editor.focus()
方法即可。此方法与文档服务方法 Document.setCursor(position)
和 Document.setSelection(range)
结合使用时特别有用。