If you need to periodically show a message or notification to a user, instead of using a time-driven trigger for calling the Class Ui, use a sidebar and client-side code, i.e. setTimeout
in a recursive function, to call a server side function that calls the Class Ui. You might also show the message in the sidebar.
In the case of spreadsheets another option might be use Spreadsheet.toast. Another option is to edit the document. This might work in small documents where the edited section is shown all the time.
When running function calling Class Ui it will fail if the corresponding document editor UI and the Google Apps Script Editor hasn't a connection between an active document, form, slide or spreadsheet and the script.
Time-Driven triggers have a connection with the container / bounded file but there isn't one with the document editor UI, no matter if the script was opened from the document editor UI at the time that the time-driven trigger was executed.
This error will happen too when calling Class Ui from a standalone project because there is no connection with a document editor user interface. While the Google Apps Script editor might look as a "document editor", Class Ui doesn't work with it as the Class Ui can only be called from DocumentApp, FormApp, SlidesApp and the SpreadsheetApp classes.
Below is a simple sample. It adds a custom menu used to open a sidebar. The sidebar holds the client-side code that will open a modal dialog every 10 seconds for 3 times. The client-side code has a timer
function that holds a setTimeout which calls the controller
function which calls the server-side function and updates a counter used to limit the number of times that the server-side function will be executed.
Steps to use this code:
- Create a new spreadsheet
- Click Extensions > Apps Script
- Remove the default content from default .gs file and add the Code.gs code.
- Add two html files and name them
sidebar
and 'modalDialog
.
- Add the html
sidebar.html
and modalDialog.html
to the corresponding files.
- Run
onOpen
or reload the spreadsheet and click My Menu > Show sidebar (reloading the spreadsheet will will close the script editor) and authorize the script.
- On the spreadsheet, click My Menu > Show sidebar
Code.gs
/**
* Adds a custom menu to show the sidebar
*/
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('My Menu')
.addItem('Show Sidebar', 'showSidebar')
.addToUi()
}
/**
* Shows the sidebar
*/
function showSidebar() {
const myHttpOutput = HtmlService.createHtmlOutputFromFile('sidebar')
.setTitle('My Sidebar')
SpreadsheetApp.getUi().showSidebar(myHttpOutput);
}
/**
* Shows the modal dialog. To be called from client-side code.
*/
function showModalDialog() {
const myHttpOutput = HtmlService.createHtmlOutputFromFile('modalDialog')
.setWidth(400)
.setHeight(150)
SpreadsheetApp.getUi().showModalDialog(myHttpOutput, 'My Modal');
}
sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<style>
.error {
color : red;
background-color : pink;
border-style : solid;
border-color : red;
}
</style>
<body>
<h1>Timed Modal Dialog Controller</h1>
<div id="sidebar-status">
The modal dialog will be shown after the specifed timeout interval.
</div>
<script>
const defaultInterval = 10000;
let count = 0;
/**
* Run initializations on sidebar load.
*/
(() => {
timer();
})();
/**
* Calls the controller function at the given interval.
*
* @param {Number} interval (optional) Time in ms between polls. Default is 2s (2000ms)
*
*/
function timer(interval) {
interval = interval || defaultInterval;
setTimeout(() => {
controller();
}, interval);
};
/**
* Calls the server side function that uses Class Ui to
* show a modal dialog.
*/
function controller(){
/** Maximum number of iterations */
const max = 3;
if(count < max){
google.script.run
.withSuccessHandler(() => {
const msg = `Counter: ${++count}`;
showStatus(msg);
timer();
})
.withFailureHandler(error => {
const msg = `<div class="error">${error.message}</div>`;
showStatus(msg);
})
.showModalDialog();
} else {
const msg = `<p>Maximum reached.</p>`;
showStatus(msg)
}
}
/**
* Displays the given status message in the sidebar.
*
* @param {String} msg The status message to display.
*/
function showStatus(msg) {
const status = document.querySelector('#sidebar-status');
status.innerHTML = msg;
}
</script>
</body>
</html>
modalDialog.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1>Attention!</h1>
<p>It's time to take a break.</p>
</body>
</html>
It can be easily adapted to be used on a document, form or presentation.
If the manifest is being edited manually, please be sure to include https://www.googleapis.com/auth/script.container.ui
in the list of OAuth scopes besides other required according to the type of document to which the script will be bounded.
If you need to work with a standalone script, instead of a bounded script, you should use it as and Editor add-on.
Reference
Related