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
andmodalDialog.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
- https://developers.google.com/apps-script/reference/base/ui
Related
- How to poll a Google Doc from an add-on
- How do I make a Sidebar display values from cells?
- Exception: Cannot call SpreadsheetApp.getUi() from this context. (line 2, file «Code»)
Google Docs Editors Help
Sign in
Google Help
- Help Center
- Community
- Google Docs Editors
- Privacy Policy
- Terms of Service
- Submit feedback
Send feedback on…
This help content & information
General Help Center experience
- Help Center
- Community
Google Docs Editors
Перейти к контенту
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
andmodalDialog.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
- https://developers.google.com/apps-script/reference/base/ui
Related
- How to poll a Google Doc from an add-on
- How do I make a Sidebar display values from cells?
- Exception: Cannot call SpreadsheetApp.getUi() from this context. (line 2, file «Code»)
I had that problem and found it involved another google script in the project set up by a different user. The error message had a mixture of details from my script and the other script:
myFunctionName Cannot call SpreadsheetApp.getUi() from this context.
(line 2, file «OtherScript«)
Line 2 of the other script did use getUi()
var app = SpreadsheetApp.getUi();
It seemed that when my script ran (triggered by onChange event) then the other script would also get run (maybe also triggered by a change event). The other script set up some UI elements, some simple menus. Normally that caused no problem. However, SpreadsheetApp.getUi() only works in the context of there being a current instance of an open editor (see https://developers.google.com/apps-script/reference/base/ui). So if a change event happened without an open editor then it fails, causing the error.
I resolved the problem by slightly modifying the other script to catch the problem:
try {
app = SpreadsheetApp.getUi();
} catch (error) {}
if (app != null) {
//Use app...
}
A different approach that might also have worked is to speak with the person and how their script was triggered, and see if they’d change it to trigger by onOpen event that just occurs when someone opens a spreadsheet and hence there is a Ui context.
So, I think your problem would be coming from SpreadsheetApp.getUi() in a different script in the project. See if the error message mentions a different file, like mine did. When your script runs in the night, there would be no context, which explains why the error occurs at that time.
Hi ! I recently discovered Google Apps Script and to learn i watched some tutorials on youtube.
This is the video I watched : https://www.youtube.com/watch?v=SnzFCC3tkZY
I just wanted to make some tests just to see how it works, so this is the scipt from the video:
function addRecord() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheets()[0];
mainSheet.appendRow(['Curt', new Date()]);
}
function startForm() {
var form = HtmlService.createHtmlOutputFromFile('AddForm');
SpreadsheetApp.getUi().showModalDialog(form, 'Add Record');
}
function addMenu() {
var menu = SpreadsheetApp.getUi().createMenu('Custom');
menu.addItem('Add Record Form', 'startForm');
menu.addToUi();
}
But when i run the addMenu function, it show me this error message :
Exception: Cannot call SpreadsheetApp.getUi() from this context.
addMenu
@ Code.gs:13
So I don’t really understand what’s the problem so if someone could help me, i would be very happy !
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.
Already on GitHub?
Sign in
to your account
Comments
I was added on google sheets and then I publish and try to test. But, I got this error. Any solution?
After upload files, I was checked on folder demo, but not found any files and I can’t got the link from google drive.
One thing to remember is that your column headers must match exactly the
form field names in upload.html
On Fri, Jul 17, 2020 at 8:09 AM Afif Alfiano <notifications@github.com>
wrote:
…
2 participants
В Таблице Гугл создается лист, который заполняется данными из стороннего приложения. После этого необходимо изменить некоторые данные в ячейках.
При вызове функции onChange в журнале триггера сбой выполнения
«Не удается вызвать функцию SpreadsheetApp.getUi() из этого контекстного меню. at [unknown function].
function createTable(
tableName,
spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
) {
try {
spreadsheet.insertSheet(tableName);
spreadsheet.getSheetByName(settings.listName).activate();
} catch (err) {
return err;
}
}
function onChange() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sheetRows = sheet.getMaxRows();
for (var row = 2; row <= sheetRows; row++) {
let current_cell_value = sheet.getRange(row, 5, 1, 1).getValue();
if (current_cell_value.includes('ааа')) {
sheet.getRange(row, 1, 1, 1).setValue('bbb');
}
}
}
Hi ! I recently discovered Google Apps Script and to learn i watched some tutorials on youtube.
This is the video I watched : https://www.youtube.com/watch?v=SnzFCC3tkZY
I just wanted to make some tests just to see how it works, so this is the scipt from the video:
function addRecord() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheets()[0];
mainSheet.appendRow(['Curt', new Date()]);
}
function startForm() {
var form = HtmlService.createHtmlOutputFromFile('AddForm');
SpreadsheetApp.getUi().showModalDialog(form, 'Add Record');
}
function addMenu() {
var menu = SpreadsheetApp.getUi().createMenu('Custom');
menu.addItem('Add Record Form', 'startForm');
menu.addToUi();
}
But when i run the addMenu function, it show me this error message :
Exception: Cannot call SpreadsheetApp.getUi() from this context.
addMenu
@ Code.gs:13
So I don’t really understand what’s the problem so if someone could help me, i would be very happy !