DialogBox in addon for Google Form - google-apps-script

I'm trying to display a DialogBox from a function used in an addon for Google Form.
function onOpen() {
FormApp.getUi()
.createMenu('MyAddon')
.addItem('Test', 'myTest')
.addToUi();
}
function myTest() {
Logger.log("--> called");
var app = UiApp.createApplication(); // or UiApp.getActiveApplication()
var dialog = app.createDialogBox();
dialog.setPopupPosition(100, 100).setSize(500, 500);
dialog.show();
return app;
}
Unlike FormApp.getUi().showModalDialog(content, title), function using DialogBox is called but does nothing (nothing is displayed).
Does anyone succeed in displaying such component in a Google addon?
Goal of using DialogBox is to be able to set a close handler, which is not possible using .showModalDialog(...) (which returns void but not reference to created dialog).
I don't see any other way in Google API for UI in addon to display a dialog, with a callback when it's closed.

Calling this function from server side code (which is what happens when that menu item is selected) will not display this Ui if it is just returned from the function. You need to use a method such as SpreadsheetApp.getUi().showModalDialog(uiInstance, 'My add-on'); to have it display for the user.
If all you're looking for is a way to take action once the dialog is closed, consider using alert dialogs or prompt dialogs. These will return an object which can be queried to see which button the user selected, or what text was entered in the dialog.

Related

Can't Get Specific .html to load on doGet() | WebApp

I am trying to load a specific .html depending on the current cell's Text (either .value() or .getdisplayValue()).
However, for some reason only the Payroll.html file is ever being fired, even though the current cell's text is "Click To Upload Receipt"
function doGet(e) {
if (sheet.getActiveCell().getValue() === "Click To Upload Receipt") {
var htmlOutput = HtmlService.createTemplateFromFile('UploadFile');
} else {
var htmlOutput = HtmlService.createTemplateFromFile('Payroll');
}
htmlOutput.message = '';
return htmlOutput.evaluate();
}
I have ReDeployed my WebApp and set the URL of this as a link on the current cell.
Please let me know if you need any specific information to assist. Thank you all.
An Apps Script webapp (container bound or not) has no concept of an "active cell," in the doGet or doPost event handler.
Instead, you should invoke the doGet endpoint with a URL query parameter containing the desired template to display. You can access this from the event object received by the doGet call.
For example,
https://scripts.google.com/a/.......?action=do%20payroll
This would invoke your doGet handler with a function argument that has a property "parameter" with the property named "action" and the value "do payroll".
You could then load the desired parameter by inspecting this value, and provide a fallback if an unknown value is provided:
function doGet(eventObj) {
const action = eventObj.parameter.action;
if (action === "do payroll") {
// Do stuff
} else if (action === "do receipt") {
// Do other stuff
} else {
// Provide fallback for incorrect user input
}
}
This may be helpful: https://developers.google.com/apps-script/guides/web
Despite that sheet has not being defined, getActiveCell() doesn't work as you assumed for Google Apps Script web apps, but it might work for a dialog of sidebar.
The above because the web application instance hasn't the "active" context. If web app code belongs to a spreadsheet bounded project, SpreadsheetApp.getActiveSpreadsheet() will return the bounded spreadsheet but methods like getActiveCell and getActiveRange will return A1 of the first sheet.
Instead you using the "active cell" you could include a query string (i.e. ?page=page_name
A very simplistic way to implement this:
function doGet(e){
const template = HtmlService.createTemplateFromFile(e.parameter.page || 'index');
return template.evaluate();
}
Related
Linking to another HTML page in Google Apps Script
Multi-page site in Google Apps Script: How to invoke doGet() through jquery or javascript?
Resources
https://developers.google.com/apps-script/guides/web

Launching Google Form from App Script

I am trying to launch an Google Form from a script on Google sheets. At the moment, i am doing it trough the form's unique ID and this is what I have:
function addtoDatabase() {
var formaddnew = FormApp.openById('uniqueformID');
}
I am sure i am using the correct form's ID. The script runs without returning any errors or exceptions but the form isn't launched. I am new to App script and I think I might be overlooking something.
Thank you for your help.
You (as me too) were confused with a strange name of function openById:
var formaddnew = FormApp.openById('uniqueformID');
This code does not "open" a form, it assigns a form to the variable formaddnew. You may check it if you add this line of code:
Logger.log(formaddnew);
run the code and press [Ctrl]+[Enter] to see the log.
How to open a form with a script
No. One has no such option because scripts have no access to a browser. A Form is actually opened in a browser, and google-apps-script cannot open new tabs in a browser.
Is there any way though to open a form from a pop-up or message box?
Please try the method described here:
Google Apps Script to open a URL
Here's a tested sample code based on this answer:
var C_URL = 'https://stackoverflow.com/questions/48947678/launching-google-form-from-app-script'; // change
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Menu')
.addItem('Show Window', 'testNew')
.addToUi();
}
function testNew(){
showAnchor('Open this link',C_URL);
}
function showAnchor(name,url) {
var html = '<html><body>'+name+'</body></html>';
var ui = HtmlService.createHtmlOutput(html)
SpreadsheetApp.getUi().showModelessDialog(ui,"demo");
}
Notes:
the script creates a custom menu and opens the window with an URL.
User has to click the URL.

Opening a modal over another one

I use Google App Script with Google Sheets. When I click a button, the code enters in this following function :
function myButtonClicked(){
const template = HtmlService
.createTemplateFromFile('Index')
.evaluate();
const html = HtmlService
.createHtmlOutput(template)
.setWidth(500)
.setHeight(500)
.setTitle('MyTitle');
SpreadsheetApp.getUi().showModalDialog(html, 'MyTitle');
}
My form contains multiple inputs that have an onclick event. This event does something and displays a message in a specific case.
The message is displayed thanks to the following function :
SpreadsheetApp.getUi().alert('myMessage');
The problem is : when this last one appears, my modal containing my form is closed.
How can I display a modal over another one ?
Thanks.

"SpreadsheetApp.getUi() cannot be called from this context"

In a Google Sheets spreadsheet, I want to show a modal dialog created from HTML, then run a function, then close that HTML prompt automatically.
The dialog should stay until the function finishes executing, then automatically disappear.
This process has to be repeated every 3 hours, and the script needs to run as me (as I have edit permissions that other users do not) so simple triggers probably won't work (I've read that you must create an installable trigger if you want the function to run as you and not whoever the current user is at the given time)
I currently have:
A .gs function Magic_Telling, that creates a modal dialog by using an HTML file
An HTML file, Prompt_Styling, that contains the css / html styling for the prompt. This HTML file then calls a .gs function All_In that processes the rows
My code:
Magic_Telling
Creates the modal dialog from HTML file.
function Magic_Telling() {
var UI = SpreadsheetApp.getUi();
var newline = '\n'
// Display a modal dialog box with custom HtmlService content.
var htmlOutput = HtmlService.createHtmlOutputFromFile('PromptStyling')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(300)
.setHeight(100);
UI.showModalDialog(htmlOutput, ' ');
}
Prompt_Styling HTML file for styling prompt + script that runs the function All_In that will process rows
<html>
<head>
// some irrelevant stuff here
</head>
<script>
window.onload = function() {
google.script.run
.withSuccessHandler(closeDialog)
.All_In();
};
window.closeDialog = function() {
google.script.host.close();
};
</script>
</html>
All_In Function to process rows
function All_In() {
UnlockRowBlocks();
UnhideRowBlocks();
LockRowBlocks();
HideRowBlocks();
}
When I run MagicTelling from the script editor, it works beautifully. The entire sequence executes (prompt shown, All_In executed, prompt disappeared). Perfect.
I then created an installable trigger by going to
Script Editor > Resources > Current project's triggers
and added a trigger to run Magic_Telling every 3 hours.
(I presume this is an "installable trigger")
But I get this error message:
Cannot call SpreadsheetApp.getUi() from this context.
...when the function reaches the first line of Magic_Telling
What should I do to get around this?
Ui Dialogs can not be called by time triggered functions, they have to be triggered by a user action, that's to say a click on a menu item or some sort of button that calls the function showing the UI.
Simple case getting the 'Cannot call SpreadsheetApp.getUi() from this context.'-Error for everybody who just got started with scripting using the Tools > Script editor Menu.
In this case you work with a standalone script only, meaning, your script is just attached to one document or spreadsheet.
The standalone script allows i.e. to simply call doc = DocumentApp.getActiveDocument(), the active Document the script is attached to.
It happened to me that I used var ui = SpreadsheetApp.getUi(); while getting the ERROR message in quest here...
and it took me hours to find out what went wrong with this simple line as I went down all the way to Oauth Scopes and the Developer Console.
So, it might be helpful for some beginners to know that I actually used the var ui = SpreadsheetApp.getUi(); within a Document-Script.
Very clear I got the error, but ...
Hope this is helpful for some simple scripters!
PS. I hope it is needless to mention that using a var ui = DocumentApp.getUi();
within a SpreadSheet will produce a similar Error message.
If this error happened then check the triggers or close the script editor tab and refresh google sheets then open the script project .
Make sure that the Apps Script is bound script and not standalone script . or the getUi() won't work .
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
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")

GAS - define listBox() displayed value

I have a form built and displayed in UiApp which uses the listBox class. My listBoxes are created as in the example below:
var yearText = app.createListBox().setName("yearText")
.addItem("")
.addItem("Reception")
.addItem("Nursery");
When the form loads for the first time they default to display blank (the value at index 0). What I am trying to do is reload the form with stored data, populating the listBox with it's saved value whilst still offering the same options as above (blank, Reception, Nursery).
I have tried using the various setValue methods available to the listBox class but I am not making any progress (variations of setValue are working fine for textArea, checkBox and dateBox classes elsewhere in the form). Any help or guidance gratefully received!
The method for that is setItemSelected(index,Boolean), example below
function doGet(){
var app = UiApp.createApplication();
var list = app.createListBox().addItem('').addItem('v2').addItem('v3').addItem('v4').setItemSelected(2,true);
app.add(list);
return app;
}
Or also : setSelectedIndex(index) (same link for doc
example below too
function doGet(){
var app = UiApp.createApplication();
var list = app.createListBox().addItem('').addItem('v2').addItem('v3').addItem('v4').setSelectedIndex(2);
app.add(list);
return app;
}