google.script.run
— это асинхронный клиентский JavaScript API, позволяющий страницам HTML-сервисов вызывать серверные функции Apps Script. В следующем примере показана самая базовая функциональность google.script.run
— вызов функции на сервере из клиентского JavaScript.
Код.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function doSomething() { Logger.log('I was called!'); }
Индекс.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> google.script.run.doSomething(); </script> </head> </html>
Если вы развернете этот скрипт как веб-приложение и перейдете по его URL-адресу, вы ничего не увидите, но если вы посмотрите журналы, то увидите, что была вызвана серверная функция doSomething()
.
Вызовы серверных функций на стороне клиента являются асинхронными: после того, как браузер запрашивает у сервера выполнение функции doSomething()
, браузер немедленно переходит к следующей строке кода, не дожидаясь ответа. Это означает, что вызовы серверных функций могут выполняться не в ожидаемом порядке. Если вы вызываете две функции одновременно, невозможно узнать, какая из них выполнится первой; результат может различаться при каждой загрузке страницы. В этой ситуации обработчики успешных и неудачных событий помогают контролировать ход выполнения кода.
API google.script.run
допускает 10 одновременных вызовов серверных функций. Если вы сделаете 11-й вызов, пока выполняются 10, серверная функция будет отложена до тех пор, пока не освободится одно из 10 мест. На практике вам редко придётся задумываться об этом ограничении, особенно учитывая, что большинство браузеров уже ограничивают количество одновременных запросов к одному серверу числом менее 10. Например, в Firefox это ограничение равно 6. Большинство браузеров аналогичным образом откладывают избыточные запросы к серверу до завершения одного из текущих запросов.
Параметры и возвращаемые значения
Вы можете вызвать серверную функцию с параметрами из клиента. Аналогично, серверная функция может вернуть значение клиенту в качестве параметра, передаваемого обработчику успешного выполнения .
Допустимые параметры и возвращаемые значения — это примитивы JavaScript, такие как Number
, Boolean
, String
или null
, а также объекты и массивы JavaScript, состоящие из примитивов, объектов и массивов. Элемент form
на странице также допустим в качестве параметра, но он должен быть единственным параметром функции и недопустим в качестве возвращаемого значения. Запросы завершатся ошибкой, если вы попытаетесь передать элемент Date
, Function
, DOM помимо form
или другой запрещённый тип, включая запрещённые типы внутри объектов или массивов. Объекты, создающие циклические ссылки, также завершатся ошибкой, а неопределённые поля внутри массивов станут null
.
Обратите внимание, что объект, переданный на сервер, становится копией оригинала. Если серверная функция получает объект и изменяет его свойства, свойства на клиенте не изменяются.
Обработчики успеха
Поскольку клиентский код переходит на следующую строку, не дожидаясь завершения серверного вызова, withSuccessHandler(function)
позволяет указать клиентскую функцию обратного вызова, которая будет запущена при ответе сервера. Если серверная функция возвращает значение, API передаёт это значение новой функции в качестве параметра.
В следующем примере отображается уведомление браузера при ответе сервера. Обратите внимание, что для этого примера кода требуется авторизация, поскольку серверная функция обращается к вашей учётной записи Gmail. Самый простой способ авторизовать скрипт — вручную запустить функцию getUnreadEmails()
из редактора скриптов перед загрузкой страницы. Кроме того, при развёртывании веб-приложения вы можете выбрать его запуск от имени «пользователя, получающего доступ к веб-приложению», в этом случае вам будет предложено авторизоваться при загрузке приложения.
Код.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getUnreadEmails() { return GmailApp.getInboxUnreadCount(); }
Индекс.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)
или укажите обработчик сбоев, который ничего не делает.
Как показывает этот пример, синтаксис обработчиков неудач практически идентичен синтаксису обработчиков успехов.
Код.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getUnreadEmails() { // 'got' instead of 'get' will throw an error. return GmailApp.gotInboxUnreadCount(); }
Индекс.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>
Пользовательские объекты
Вы можете повторно использовать один и тот же обработчик успешного или неудачного выполнения для нескольких вызовов сервера, вызвав метод withUserObject(object)
для указания объекта, который будет передан обработчику в качестве второго параметра. Этот «объект пользователя» (не путать с классом User
) позволяет реагировать на контекст, в котором клиент обратился к серверу. Поскольку объекты пользователя не отправляются на сервер, они могут быть практически чем угодно, включая функции, элементы DOM и т. д., без ограничений на параметры и возвращаемые значения для серверных вызовов. Однако объекты пользователя не могут быть объектами, созданными с помощью оператора new
.
В этом примере нажатие любой из двух кнопок обновит значение этой кнопки, используя значение с сервера, а значение другой кнопки останется неизменным, несмотря на то, что у них общий обработчик успешного нажатия. Внутри обработчика onclick
ключевое слово this
относится к самой button
.
Код.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getEmail() { return Session.getActiveUser().getEmail(); }
Индекс.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 Диск, а затем выводится URL-адрес файла на клиентской странице. Внутри обработчика onsubmit
ключевое слово this
относится к самой форме. Обратите внимание, что при загрузке всех форм на странице действие отправки по умолчанию отключено с помощью preventFormSubmit
. Это предотвращает перенаправление страницы на неточный URL-адрес в случае возникновения исключения.
Код.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function processForm(formObject) { var formBlob = formObject.myFile; var driveFile = DriveApp.createFile(formBlob); return driveFile.getUrl(); }
Индекс.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()
полностью невидимы для клиента.
Код.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 } };
Индекс.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 Docs, Sheets или Forms можно изменять, вызывая методы google.script.host
setWidth(width)
или setHeight(height)
в клиентском коде. (Чтобы задать начальный размер диалогового окна, используйте методы HtmlOutput
setWidth(width)
и setHeight(height)
.) Обратите внимание, что при изменении размера диалоговые окна не центрируются повторно в родительском окне, и невозможно изменить размер боковых панелей .
Закрытие диалоговых окон и боковых панелей в Google Workspace
Если вы используете службу HTML для отображения диалогового окна или боковой панели в Google Docs, Sheets или Forms, вы не сможете закрыть интерфейс вызовом window.close()
. Вместо этого необходимо вызвать google.script.host.close()
. Пример см. в разделе об использовании HTML в качестве пользовательского интерфейса Google Workspace .
Перемещение фокуса браузера в Google Workspace
Чтобы переключить фокус браузера пользователя с диалогового окна или боковой панели обратно на редактор Google Docs, Sheets или Forms, просто вызовите метод google.script.host.editor.focus()
. Этот метод особенно полезен в сочетании с методами службы Document Document.setCursor(position)
и Document.setSelection(range)
.