Google Apps Script

UI Service

The UI service in Google Apps Script provides the ability to build a user interface for displaying or capturing information using user interface elements called widgets.

  1. Overview
  2. Available User Interface Elements
  3. Designing the User Interface
  4. Creating User Interface Elements
  5. Using Server Handlers
  6. Using Client Handlers
  7. Using Validators
  8. Using Forms
  9. Deploying a User Interface as a Web App
  10. Displaying a User Interface from a Spreadsheet
  11. Using Custom HTML

Overview

Your scripts can create a wide range of user interface elements, including, but not limited to, the following:

After you put these elements together, the result is a working user interface. Your scripts can display the user interface in the following three ways:

Viewing the Available User Interface Elements

You can create a large number of user interface elements using Google Apps Script.

To see all the available elements:

  1. Open the Script Editor
  2. Type in the following instructions; do not copy and paste:
    function myFunction() {
      var app = UiApp.createApplication();
      var button = app.
    }
    You see a menu listing all available elements that you can create, as well as the arguments required for each element. If you don't see a menu listing, it may be because you pasted in the code. The built-in code autocomplete functionality in the editor requires you to type the trailing period that follows app.

You can also see all the available methods for creating user interface elements in the documentation for the UiInstance class.

Designing the User Interface

Before you start writing the script to display the user interface, do the following:

  1. Plan the script. What tasks should the script accomplish?
  2. Write down the specific information you want to display to or collect from your users.
  3. Draw the user interface and make notes about all the interface elements you must create with your script.
  4. Determine what the script and interface should do in response to any user input.
  5. Determine the conditions for exiting the script.

Creating User Interface Elements

Before you can create user interface elements such as buttons or dialog boxes, you need a UiInstance object to contain the user interface elements. After you create the UiInstance object, you can add buttons, dialog boxes, panels, and other elements to the UiInstance object.

The general syntax for these operations is as follows:

  • To create a UiInstance object, use the syntax var your_app_name = UiApp.createApplication();, where your_app_name is the name you assign.
  • To create a user interface element and associate it with your UiInstance object, use the syntax var your_ui_element_name= your_app_name.createElement_Name();.
  • To add one user interface element to another, for example to display a button on a panel, use the syntax your_ui_element_name1.add(your_ui_element_name2);

The following statement creates an UiInstance object called myapp by calling the createApplication method of the UiApp class:

var myapp = UiApp.createApplication();

You can now create some user interface elements. Here's a button with the text Press Me on it:

var mybutton = myapp.createButton('Press Me');

The text you want displayed on a button is passed in as an argument.

You might want a panel to hold the button. The following creates a vertical panel.

var mypanel = myapp.createVerticalPanel();

There are other kinds of panels, too: stack panels, focus panels, form panels, and so on.

Here's the code for displaying your button on the panel:

mypanel.add(mybutton);

Next, add the panel to the application:

myapp.add(mypanel);

Lastly, you need to instruct Google Apps Script to display the interface elements:

return myapp;

You can create the user interface elements in any order. The order in which you add elements to the objects determines the display order. For example, if you create a VerticalPanel object, and add elements a, b and c to the panel, the elements are displayed from top to bottom in that order. However, you can create a, b, and c in any order. Creating the elements and adding them to your application are separate steps requiring separate instructions.

Here's a short script that does nothing but display a panel with a button on it. You can follow what each line does in the comments.

function doGet() { // A script with a user interface that is published as a web app
  // must contain a doGet(e) function.

  // Create the UiInstance object myapp and set the title text
  var myapp = UiApp.createApplication().setTitle('Here is the title bar');

  // Create a button called mybutton and set the button text
  var mybutton = myapp.createButton('Here is a button');

  // Create a vertical panel called mypanel and add it to myapp
  var mypanel = myapp.createVerticalPanel();

  // Add mybutton to mypanel
  mypanel.add(mybutton);

  // Add my panel to myapp
  myapp.add(mypanel);

  // return myapp to display the UiInstance object and all elements associated with it.
  return myapp;
}

You can chain together setter methods, which are methods that set values on objects. In the script above, the instruction var myapp = UiApp.createApplication().setTitle('Here is the title bar'); creates a UiInstance object and sets its title. You might also set the size of the object:

var myapp = UiApp.createApplication().setTitle('Here is the title bar').setHeight(50).setWidth(100);

Here's another short script that displays a panel but doesn't do anything else. It shows how to use Grid objects and the setWidget method to create a more complex layout and also how to create text boxes and label them. You can enter text into the text boxes, but pressing the button does nothing.

The grid size of 3 x 2 is defined in the instruction var mygrid = myapp.createGrid(3, 2); and each setWidget method defines the element displayed at each position within the grid. Note that the position definitions use a zero-based index, so that rows are identified with the numerals 0, 1, and 2 rather than 1, 2, and 3.

function demoUI() {
  var myapp = UiApp.createApplication().setTitle('An improved GUI');

  var mygrid = myapp.createGrid(3, 2);
  mygrid.setWidget(0, 0, myapp.createLabel('Name:'));
  mygrid.setWidget(0, 1, myapp.createTextBox());
  mygrid.setWidget(1, 0, myapp.createLabel('Age:'));
  mygrid.setWidget(1, 1, myapp.createTextBox());
  mygrid.setWidget(2, 0, myapp.createLabel('City'));
  mygrid.setWidget(2, 1, myapp.createTextBox());

  var mybutton = myapp.createButton('Press me');
  var mypanel = myapp.createVerticalPanel();
  mypanel.add(mygrid);
  mypanel.add(mybutton);
  myapp.add(mypanel);
  return myapp;
}​

Using Server Handlers

The two scripts in the section Creating User Interface Elements display a panel with various controls, but they don't do anything else. To make a user interface useful, you need the ability to accept input from a user and update the user interface.

Here's a simple example that responds to a button being clicked by making a label visible.

function doGet(e) {
  var app = UiApp.createApplication();

  var button = app.createButton('Click Me');
  app.add(button);

  var label = app.createLabel('The button was clicked.')
                 .setId('statusLabel')
                 .setVisible(false);
  app.add(label);

  var handler = app.createServerHandler('myClickHandler');
  button.addClickHandler(handler);

  return app;
}

function myClickHandler(e) {
  var app = UiApp.getActiveApplication();

  var label = app.getElementById('statusLabel');
  label.setVisible(true);

  app.close();
  return app;
}

The doGet function is the one that is run when a user accesses the URL at which the script is deployed. Though it's not used in this simple example, notice that doGet takes a parameter, e. This e parameter contains any information that was passed in the URL's query string. For more details, see doGet Parameters below.

  var handler = app.createServerHandler('myClickHandler');
  button.addClickHandler(handler);

The instruction var handler = app.createServerHandler('myClickHandler'); creates a server-side click handler object called handler as part of the UiInstance application called app . A click handler performs an action in response to a mouse click. Server-side means that the actions are performed by a server, in this case Google's Apps Script server. The argument ('myClickHandler' ) means the handler action is to run function myClickHandler() .

You can add multiple click handlers to a button. The calls to the click handlers are asynchronous. That means that after the browser has requested that the server run a click handler function, it doesn't wait for a response, and continues immediately to the next line of code. This is important, because browser JavaScript is single-threaded, and if the code waited for the server to respond, nothing else could happen until the response came back, including users clicking on buttons in the app or doing anything else. This also means that server function calls may not execute in the order you expect.

The instruction button.addClickHandler(handler); associates the click handler to the already-defined button object called button .

By calling setId('statusLabel') on the label widget, we will be able to access it by its id in the myClickHandler, as you'll see below.

The second function in the script, myClickHandler() , gets a reference to the active application, and then gets access to the label whose id we set to statusLabel in the doGet function. Once the function has access to that label, it can call setVisible(true) to display it. Then the function closes the application app , and, finally, return the script to its starting point.

Here's a longer and more complex script. This script collects some information from text fields on a panel and writes that information into a Spreadsheet. See the inline comments for details.

function doGet(e) {
  var doc = SpreadsheetApp.openById(SPREADSHEET_ID_GOES_HERE);
  var app = UiApp.createApplication().setTitle('New app');
  // Create a grid with 3 text boxes and corresponding labels
  var grid = app.createGrid(3, 2);
  grid.setWidget(0, 0, app.createLabel('Name:'));

  // Text entered in the text box is passed in to userName
  // The setName method will make those widgets available by
  // the given name to the server handlers later
  grid.setWidget(0, 1, app.createTextBox().setName('userName'));
  grid.setWidget(1, 0, app.createLabel('Age:'));
  grid.setWidget(1, 1, app.createTextBox().setName('age'));

  // Text entered in the text box is passed in to age
  grid.setWidget(2, 0, app.createLabel('City'));
  grid.setWidget(2, 1, app.createTextBox().setName('city'));

  // Text entered in the text box is passed in to city.

  // Create a vertical panel..
  var panel = app.createVerticalPanel();

  // ...and add the grid to the panel
  panel.add(grid);

  // Create a button and click handler; pass in the grid object as a callback element and the handler as a click handler
  // Identify the function b as the server click handler

  var button = app.createButton('submit');
  var handler = app.createServerHandler('b');
  handler.addCallbackElement(grid);
  button.addClickHandler(handler);

  // Add the button to the panel and the panel to the application, then display the application app
  panel.add(button);
  app.add(panel);
  return app;
}

// Function that records the values in a Spreadsheet
function b(e) {
  var doc = SpreadsheetApp.openById(SPREADSHEET_ID_GOES_HERE);
  var lastRow = doc.getLastRow(); // Determine the last row in the Spreadsheet that contains any values
  var cell = doc.getRange('a1').offset(lastRow, 0); // determine the next free cell in column A
  // You can access e.parameter.userName because you used setName('userName') above and
  // also added the grid containing those widgets as a callback element to the server
  // handler.
  cell.setValue(e.parameter.userName); // Set the value of the cell to userName
  cell.offset(0, 1).setValue(e.parameter.age); // Set the value of the adjacent cell to age
  cell.offset(0, 2).setValue(e.parameter.city); // set the value of the next cell to city

  // Clean up - get the UiInstance object, close it, and return
  var app = UiApp.getActiveApplication();
  app.close();
  // The following line is REQUIRED for the widget to actually close.
  return app;
}​

Here's the same script, with functions added that enable the form to be used multiple times before a user chooses to exit. The in-line comments explain the new instructions.

function doGet(e) {
  var doc = SpreadsheetApp.openById(SPREADSHEET_ID_GOES_HERE);
  var app = UiApp.createApplication().setTitle('New app');

  // Create the entry form, a 3 x 2 grid with text boxes for name, age, and city that is then added to a vertical panel
  var grid = app.createGrid(3, 2);
  grid.setWidget(0, 0, app.createLabel('Name:'));
  grid.setWidget(0, 1, app.createTextBox().setName('userName').setId('userName'));
  grid.setWidget(1, 0, app.createLabel('Age:'));
  grid.setWidget(1, 1, app.createTextBox().setName('age').setId('age'));
  grid.setWidget(2, 0, app.createLabel('City'));
  grid.setWidget(2, 1, app.createTextBox().setName('city').setId('city'));

  // Create a vertical panel and add the grid to the panel
  var panel = app.createVerticalPanel();

  panel.add(grid);

  // Here's where this script diverges from the previous script.
  // We create a horizontal panel called buttonPanel to hold two buttons, one for submitting the contents of the form
  // to the Spreadsheet, the other to close the form.

  var buttonPanel = app.createHorizontalPanel();

  // Two buttons get added to buttonPanel: button (for submits) and closeButton (for closing the form)
  // For the submit button we create a server click handler submitHandler and pass submitHandler to the button as a click handler.
  // the function submit gets called when the submit button is clicked.
  var button = app.createButton('submit');
  var submitHandler = app.createServerClickHandler('submit');
  submitHandler.addCallbackElement(grid);
  button.addClickHandler(submitHandler);
  buttonPanel.add(button);

  // For the close button, we create a server click handler closeHandler and pass closeHandler to the close button as a click handler.
  // The function close is called when the close button is clicked.
  var closeButton = app.createButton('close');
  var closeHandler = app.createServerClickHandler('close');
  closeButton.addClickHandler(closeHandler);
  buttonPanel.add(closeButton);


  // Create label called statusLabel and make it invisible; add buttonPanel and statusLabel to the main display panel.
  var statusLabel = app.createLabel().setId('status').setVisible(false);
  panel.add(statusLabel);

  panel.add(buttonPanel);

  app.add(panel);
  return app;
}

// Close everything return when the close button is clicked
function close() {
  var app = UiApp.getActiveApplication();
  app.close();
  // The following line is REQUIRED for the widget to actually close.
  return app;
}

// function called when submit button is clicked
function submit(e) {

  // Write the data in the text boxes back to the Spreadsheet
  var doc = SpreadsheetApp.openById(SPREADSHEET_ID_GOES_HERE);
  var lastRow = doc.getLastRow();
  var cell = doc.getRange('a1').offset(lastRow, 0);
  cell.setValue(e.parameter.userName);
  cell.offset(0, 1).setValue(e.parameter.age);
  cell.offset(0, 2).setValue(e.parameter.city);

  // Clear the values from the text boxes so that new values can be entered
  var app = UiApp.getActiveApplication();
  app.getElementById('userName').setValue('');
  app.getElementById('age').setValue('');
  app.getElementById('city').setValue('');
  // Make the status line visible and tell the user the possible actions
  app.getElementById('status').setVisible(true).setText('User ' + e.parameter.userName + ' entered.' +
  'To add another, type in the information and click submit. To exit, click close.');
  return app;
}​

doGet Parameters

The doGet function has two parameters that are useful if you want to pass query string parameters to your web app's URL. The first parameter is queryString, and its value will be a string containing the query string values that were appended to the URL. For example, its value may look something like this: a=1&b=2&c=3. The second parameter is called parameter, and it is an Object containing each parameter and value pair. Its value may look something like this:

{"a": "1", "b": "2", "c": 3}

Using Client Handlers

The click handlers discussed in the previous section are server-side handlers. They require a round trip from the user's browser to the server and back to the browser, so they may not be responsive enough for some situations. Client handlers enable your script to respond to any event in a browser without connecting to the server. What you can do in response to an event is limited to a set of predefined common actions, but you still have a lot of flexibility in making your app more responsive. In the previous example, the user may click the submit button several times causing the submit function to called several times. This becomes a more serious problem when the network connection is slow, and the user gets no immediate feedback for clicking the button.

To solve this problem, we can create handlers that are executed in the browser without calling the server. Using Client Handlers, your application can now respond to events in the browser without the need to perform a round trip to Google Apps Script servers. These client-side handlers are limited to a small set of actions for security reasons, but they solve many common problems, such as the one described above.

In a similar example, you may want to provide your users with instant feedback within your app. Imagine that your user has typed text where a number is expected. Ideally, you would want to warn users as they type the value, instead of waiting until the form is submitted. Having a server event handler for each keystroke is definitely overkill for such a simple and common task. These use cases are now supported with Google Apps Script's new client handlers and validators!

This simple application enables the user to click a button to display the “Hello world” message:

function doGet() {
  var app = UiApp.createApplication();
  var button = app.createButton("Say Hello");

  // Create a label with the "Hello World!" text and hide it for now
  var label = app.createLabel("Hello World!").setVisible(false);

  // Create a new handler that does not require the server.
  // We give the handler two actions to perform on different targets.
  // The first action disables the widget that invokes the handler
  // and the second displays the label.
  var handler =
    app.createClientHandler().forEventSource().setEnabled(false).forTargets(
      label).setVisible(true);

  // Add our new handler to be invoked when the button is clicked
  button.addClickHandler(handler);

  app.add(button);
  app.add(label);
  return app;
}

The client handlers in the above example are setup in two steps.

  1. Create a client handler just as we would create a server handler
  2. Define the target widget for this handler. The target widget is the widget on which the handler will take action. We set the handler’s target in one of two ways: (a) By using the forTargets method to define the target widgets. (b) By using the forEventSource method which lets widgets wire themselves to the client handler.

In the above example, we set the handler’s target to be the event source, so that it will apply to the button that is clicked. Finally, we define the action that the handler should take, in this case disabling the button using setEnabled(false). Aside from setEnabled, you can also change styles using setStyleAttribute, change text using setText, and so on. One client handler can perform multiple actions - just chain them together - and you can even change the target so that some actions apply to one set of widgets and some actions to another set. In our example, along with disabling the button, we set the handler to display the label when it is invoked, using setVisible.

Using Validators

Validators allow both client and server handlers to check simple conditions before they are invoked. For example, the following simple application adds two numbers given by the user, while using validators to make sure the server is only called if both of the text boxes contain numbers.

function doGet() {
  var app = UiApp.createApplication();

  // Create input boxes and button
  var textBoxA = app.createTextBox().setId('textBoxA').setName('textBoxA');
  var textBoxB = app.createTextBox().setId('textBoxB').setName('textBoxB');
  var addButton = app.createButton("Add");

  // Create a handler to call the adding function
  // Two validations are added to this handler so that it will
  // only invoke 'add' if both textBoxA and textBoxB contain
  // numbers
  var handler =
    app.createServerClickHandler('add').validateNumber(textBoxA).validateNumber(
      textBoxB).addCallbackElement(textBoxA).addCallbackElement(textBoxB);

  addButton.addClickHandler(handler);

  app.add(textBoxA);
  app.add(textBoxB);
  app.add(addButton);
  return app;
}

function add(e) {
  var app = UiApp.getActiveApplication();
  var result =
    parseFloat(e.parameter.textBoxA) + parseFloat(e.parameter.textBoxB);
  var newResultLabel = app.createLabel("Result is: " + result);
  app.add(newResultLabel);
  return app;
}

There’s a variety of validators to choose from and perform different tasks. You can verify the input to be a number, an integer, an e-mail address, of specific length or with a numerical value in a defined range. You can also use general regular expressions, and lastly each validator has its negation. Note that validators work with both client and server handlers.

Validators and Client handlers work best together. For example, in our addition example above, the “Add” button should be disabled as long as the current input is not numeric. We would also like to let the user know why the button is disabled by displaying an error message. To do so, we combine the power of server handlers, client handlers, and validators in the following way:

function doGet() {
  var app = UiApp.createApplication();

  // Create input boxes and button.
  var textBoxA = app.createTextBox().setId('textBoxA').setName('textBoxA');
  var textBoxB = app.createTextBox().setId('textBoxB').setName('textBoxB');
  var addButton = app.createButton("Add").setEnabled(false);
  var label = app.createLabel("Please input two numbers");

  // Create a handler to call the adding function.
  // Two validations are added to this handler so that it will
  // only invoke 'add' if both textBoxA and textBoxB contain
  // numbers.
  var handler =
    app.createServerClickHandler('add').validateNumber(textBoxA).validateNumber(
      textBoxB).addCallbackElement(textBoxA).addCallbackElement(textBoxB);

  // Create a handler to enable the button if all input is legal
  var onValidInput =
    app.createClientHandler().validateNumber(textBoxA).validateNumber(textBoxB).forTargets(
      addButton).setEnabled(true).forTargets(label).setVisible(false);

  // Create a handler to mark invalid input in textBoxA and disable the button
  var onInvalidInput1 =
    app.createClientHandler().validateNotNumber(textBoxA).forTargets(addButton).setEnabled(
      false).forTargets(textBoxA).setStyleAttribute("color", "red").forTargets(
      label).setVisible(true);

  // Create a handler to mark the input in textBoxA as valid
  var onValidInput1 =
    app.createClientHandler().validateNumber(textBoxA).forTargets(textBoxA).setStyleAttribute(
      "color",
      "black");

  // Create a handler to mark invalid input in textBoxB and disable the button
  var onInvalidInput2 =
    app.createClientHandler().validateNotNumber(textBoxB).forTargets(addButton).setEnabled(
      false).forTargets(textBoxB).setStyleAttribute("color", "red").forTargets(
      label).setVisible(true);

  // Create a handler to mark the input in textBoxB as valid
  var onValidInput2 =
    app.createClientHandler().validateNumber(textBoxB).forTargets(textBoxB).setStyleAttribute(
      "color",
      "black");

  // Add all the handlers to be called when the user types in the text boxes
  textBoxA.addKeyUpHandler(onInvalidInput1);
  textBoxB.addKeyUpHandler(onInvalidInput2);
  textBoxA.addKeyUpHandler(onValidInput1);
  textBoxB.addKeyUpHandler(onValidInput2);
  textBoxA.addKeyUpHandler(onValidInput);
  textBoxB.addKeyUpHandler(onValidInput);
  addButton.addClickHandler(handler);

  app.add(textBoxA);
  app.add(textBoxB);
  app.add(addButton);
  app.add(label);
  return app;
}

function add(e) {
  var app = UiApp.getActiveApplication();
  var result =
    parseFloat(e.parameter.textBoxA) + parseFloat(e.parameter.textBoxB);
  var newResultLabel = app.createLabel("Result is: " + result);
  app.add(newResultLabel);
  return app;
}

Using Forms

As an alternative to using server handlers, there are some cases where you may want to use forms by creating a FormPanel instead. A FormPanel can be used with a SubmitButton to post form values to the server. All children of this panel (direct, or even children of sub-panels) that have a setName function and have been given a name will have their values sent to the server when the form is submitted. The submit can be handled in the special doPost function, as shown in the example. Note that this panel can contain at most one direct child widget. To add more children, make the child of this panel a different panel that can contain more than one child. Here is an example of how to use this widget:

 function doGet() {
   var app = UiApp.createApplication();
   var form = app.createFormPanel();
   var flow = app.createFlowPanel();
   flow.add(app.createTextBox().setName("textBox"));
   flow.add(app.createListBox().setName("listBox").addItem("option 1").addItem("option 2"));
   flow.add(app.createSubmitButton("Submit"));
   form.add(flow);
   app.add(form);
   return app;
 }

 function doPost(eventInfo) {
   var app = UiApp.getActiveApplication();
   app.add(app.createLabel("Form submitted. The text box's value was '" +
       eventInfo.parameter.textBox +
       "' and the list box's value was '" +
       eventInfo.parameter.listBox + "'"));
   return app;
 }

Deploying a User Interface as a Web App

Scripts that have user interfaces that are built using the UI service can be deployed as web apps. The process for deploying a script as a web app is described fully in the Web Apps document. In short, you will choose which version of your script you want to deploy, whether the script will execute as you (the script owner) or as the active user accessing the web app, and who can access the web app. Scripts that can be deployed as web apps contain a specially-named doGet function, which is the function that is executed when a user visits the web app's URL. The doGet function will always return an UiInstance object.

Displaying a User Interface from a Spreadsheet

As an alternative to deploying your user interface as a standalone web app, you can create a container-bound script from a Spreadsheet, and display the user interface from the Spreadsheet. To do this, find your doGet function and simply replace the call to

return app;
with the following:
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);

where app is the variable name for the UiInstance object you are returning. Additionally, when you display the user interface from a Spreadsheet, the function does not have to be named doGet. You could instead call it something like displayMyUi, and then call that function directly to display the user interface in your Spreadsheet. When a user interface is displayed from a Spreadsheet, the script runs as the user who's accessing the Spreadsheet.

Using Custom HTML

Using UiApp's HTML widget it's possible to add custom HTML markup to your application. There are restrictions on the type of content that can be added to this widget however, and only the following set of HTML tags can be used:

b, blockquote, body, br, center, caption, cite, code, div, em, h1, h2, h3, h4, h5, h6, hr, i, label, legend, li, ol, p, span, strong, sub, sup, table, tbody, td, thead, title, tr, tt, ul

To take advantage of the full set of HTML features, consider using the HTML service to build your user interface instead of UiApp.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.