google.script.run
is an asynchronous
client-side JavaScript API that allows HTML-service pages to call server-side
Apps Script functions. The following example shows the most basic functionality
of google.script.run
—
calling a function on the server
from client-side 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>
If you deploy this script as a web app and visit its URL, you won’t see
anything, but if you view the logs, you'll see that the server function
doSomething()
was called.
Client-side calls to server-side functions are asynchronous: after the browser
requests that the server run the function doSomething()
, the browser continues
immediately to the next line of code without waiting for a response. This means
that server function calls may not execute in the order you expect. If you make
two function calls at the same time, there is no way to know which function will
run first; the result may differ each time you load the page. In this situation,
success handlers and failure handlers
help control the flow of your code.
The google.script.run
API allows 10 concurrent calls to server functions. If
you make an 11th call while 10 are still running, the server function will be
delayed until one of the 10 spots is freed. In practice, you should rarely have
to think about this restriction, especially since most browsers already limit
the number of concurrent requests to the same server at a number lower than 10.
In Firefox, for example, the limit is 6. Most browsers similarly delay excess
server requests until one of the existing requests has completed.
Parameters and return values
You can call a server function with parameters from the client. Similarly, a server function can return a value to the client as a parameter passed to a success handler.
Legal parameters and return values are JavaScript primitives like a Number
,
Boolean
, String
, or null
, as well as JavaScript objects and arrays that
are composed of primitives, objects and arrays. A form
element within the page
is also legal as a parameter, but it must be the function’s only parameter, and
it is not legal as a return value. Requests fail if you attempt to pass a
Date
, Function
, DOM element besides a form
, or other prohibited type,
including prohibited types inside objects or arrays. Objects that create
circular references will also fail, and undefined fields within arrays become
null
.
Note that an object passed to the server becomes a copy of the original. If a server function receives an object and changes its properties, the properties on the client are not affected.
Success handlers
Because client-side code continues to the next line without waiting for a server
call to complete,
withSuccessHandler(function)
allows you to specify a client-side callback function to run when the server
responds. If the server function returns a value, the API passes the value to
the new function as a parameter.
The following example displays a browser alert when the server responds. Note
that this code sample requires authorization because the server-side function is
accessing your Gmail account. The simplest way to authorize the script is to run
the getUnreadEmails()
function manually from the script editor once before you
load the page. Alternately, when you
deploy the web app, you can choose
to execute it as the “user accessing the web app,” in which case you will be
prompted for authorization when you load the app.
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>
Failure handlers
In case the server fails to respond or throws an error,
withFailureHandler(function)
allows you to specify a failure handler instead of a success handler, with the
Error
object (if any) passed as an argument.
By default, if you don't specify a failure handler, failures are logged to the
JavaScript console. To override this, call withFailureHandler(null)
or supply
a failure handler that does nothing.
The syntax for failure handlers is nearly identical to success handlers, as this example shows.
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 objects
You can reuse the same success or failure handler for multiple calls to the
server by calling
withUserObject(object)
to specify an object that will be passed to the handler as a second parameter.
This “user object” — not to be confused with the
User
class — lets you respond to the
context in which the client contacted the server. Because user objects are not
sent to the server, they can be almost anything, including functions, DOM
elements, and so forth, without the restrictions on parameters and return values
for server calls. User objects cannot, however, be objects constructed with the
new
operator.
In this example, clicking either of two buttons will update that button with a
value from the server while leaving the other button unchanged, even though they
share one success handler. Inside the onclick
handler, the keyword this
refers to the button
itself.
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>
Forms
If you call a server function with a form
element as a parameter, the form
becomes a single object with field names as keys and field values as values. The
values are all converted to strings, except for the contents of file-input
fields, which become Blob
objects.
This example processes a form, including a file-input field, without reloading
the page; it uploads the file to Google Drive and then prints the URL for the
file in the client-side page. Inside the onsubmit
handler, the keyword this
refers to the form itself. Note that upon loading all forms in the page have
the default submit action disabled by preventFormSubmit
. This prevents the
page from redirecting to an inaccurate URL in the event of an exception.
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>
Script runners
You can think of google.script.run
as a builder for a “script runner.” If you
add a success handler, failure handler, or user object to a script runner, you
aren't changing the existing runner; instead, you get back a new script runner
with new behavior.
You can use any combination and any order of withSuccessHandler()
,
withFailureHandler()
, and withUserObject()
. You can also call any of the
modifying functions on a script runner that already has a value set. The new
value simply overrides the previous value.
This example sets a common failure handler for all three server calls, but two separate success handlers:
var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);
myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();
Private functions
Server functions whose names end with an underscore are considered private.
These functions cannot be called by google.script
and their names are never
sent to the client. You can thus use them to hide implementation details that
need to be kept secret on the server. google.script
is also unable to see
functions within libraries and functions which aren't
declared at the top level of the script.
In this example, the function getBankBalance()
is available in the client
code; a user who inspects your source code can discover its name even if you
don't call it. However, the functions deepSecret_()
and obj.objectMethod()
are completely invisible to
the client.
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>
Resizing dialogs in applications
Custom dialog boxes in Google Docs, Sheets, or
Forms can be resized by calling the
google.script.host
methods
setWidth(width)
or
setHeight(height)
in
client-side code. (To set the initial size of a dialog, use the HtmlOutput
methods
setWidth(width)
and
setHeight(height)
.)
Note that dialogs do not re-center in the parent window when resized, and it is
not possible to resize sidebars.
Closing dialogs and sidebars in
If you use the HTML service to display a
dialog box or sidebar in Google Docs, Sheets, or
Forms, you cannot close the interface by calling window.close()
. Instead, you
must call
google.script.host.close()
.
For an example, see the section on
serving HTML as a user interface.
Moving browser focus in
To switch focus in the user's browser from a dialog or sidebar back to the
Google Docs, Sheets, or Forms editor, simply call the method
google.script.host.editor.focus()
.
This method is particularly useful in combination with the
Document service methods
Document.setCursor(position)
and
Document.setSelection(range)
.