What I'm Trying To Do
I'm trying to add HTML to a modal dialog box in google Forms using a click event to trigger a google.script.run.withSuccessHandler() call to supply the new HTML in order to get additional user input.
GS Code
function onOpen(e) {
FormApp.getUi().createMenu("My Menu").addItem('Set Up Invites', 'setUpInvite').addToUi();
}
function setUpInvite() {
//this is the main function
var ui = HtmlService.createHtmlOutputFromFile("Index")
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle("Setup");
FormApp.getUi().showModalDialog(ui, "Setup");
}
function getEventQAnswer(answer) {
var html;
switch(answer)
{
case "yes":
//TODO
//get the event info
return "";
break;
case "no":
//create the event
html = HtmlService.createHtmlOutputFromFile("createEvent.html")
return html;
break;
}
}
HTML Index Page
On this page I'm trying to change the get the functions to work onclick. I tried initially onchange, but it still didn't work. It starts with getSelectAnswer which gets the value from the select question, then calls the GS function getEventQAnswer which gets the proper HTML from the server side and delivers it to function addHTMLChoice. However, at present, it doesn't seem to do anything.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<script>
//document.getElementById("eventQ").addEventListener("onchange", getSelectAnswer);
document.getElementById("eventQ").onclick.getSelectAnswer();
function addHTMLChoice(html) {
var div = document.getElementById('eventInfo');
div.innerHTML = html;
}
function getSelectAnswer() {
var e = document.getElementById('eventQ');
var val = e.options[e.selectedIndex].value;
google.script.run.withSuccessHandler(addHTMLChoice).getEventQAnswer(val);
}
</script>
<form>
<div>
<select id="eventQ">
<option value="yes">Yes</option>
<option value="no">No, create one now</option>
</select>
</div>
<div id="eventInfo">
</div>
</form>
</body>
</html>
This is the createEvent.html I'm trying to return in test.
<div>
<input id="datetime" name="datetime" type="datetime-local">
<p>hi</p>
</div>
The server-side code can only return certain type of parameters, described here. Since you are trying to return an html object it is not passed to the client side (your Modal dialog). Hence, modify your server-side like so:
function getEventQAnswer(answer) {
var html;
switch(answer)
{
case "yes":
//TODO
//get the event info
return "";
break;
case "no":
//create the event
html = HtmlService.createHtmlOutputFromFile("createEvent.html").asTemplate().getRawContent()
return html;
break;
}
}
Note the conversion into RawContent.
Also, I find it easier to setup onchange event trigger to obtain the choice, like so:
<form>
<div>
<select id="eventQ" onchange ='getSelectAnswer()'>
<option value="yes">Yes</option>
<option value="no">No, create one now</option>
</select>
</div>
The final html index code will be:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<script>
//document.getElementById("eventQ").addEventListener("onchange", getSelectAnswer);
//document.getElementById("eventQ").onclick.getSelectAnswer();
function addHTMLChoice(html) {
console.log("success")
console.log(html)
var div = document.getElementById('eventInfo');
div.innerHTML = html;
}
function getSelectAnswer() {
console.log("getting selected Answer")
var e = document.getElementById('eventQ');
var val = e.options[e.selectedIndex].value;
console.log(val)
google.script.run.withSuccessHandler(addHTMLChoice).withFailureHandler(failed).getEventQAnswer(val);
}
function failed(e){
console.log("Failed")
console.log(e)
}
</script>
<form>
<div>
<select id="eventQ" onchange ='getSelectAnswer()'>
<option value="yes">Yes</option>
<option value="no">No, create one now</option>
</select>
</div>
<div id="eventInfo">
</div>
</form>
</body>
</html>
Note, the use of console.log to debug on the client side. This would be useful in future for your own debugging.
Hope that helps.
Related
I am working on a Google Sheets macro that displays some text to the user, then presents some buttons for the user to interact with. The buttons run another function and I am struggling with how to have the button display the text to the user.
I can't find the method or object I am supposed to use to grab the currently open window and edit the html to add more text. Or if that isn't possible, how can I close the open window and then display a new window that also has the old text?
function example(text,title,height=90,width=350) {
const dialogbutton = '<br><input type="button" value="Do Stuff" onClick="google.script.run.doStuff();" />'
var html=HtmlService.createHtmlOutput(text+dialogbutton).setHeight(height).setWidth(width)
SpreadsheetApp.getUi().showModelessDialog(html, title);
}
function doStuff() {
const dialogWindow = ???//I am hoping to retrieve the open window as an object
const text = getText() //run some other function and get the new text to insert
dialogWindow.displayedText += text //modify the displayed window to add the new text
}
Here is a very simple example of how to communicate with the server (i.e. Spreadsheet or Doc). In this case a spreadsheet with Sheet1!A1 = hello
Here is a simple dialog
Server side code Code.gs bound to a spreadsheet
function showTest() {
var html = HtmlService.createTemplateFromFile("HTML_Simple");
html = html.evaluate();
SpreadsheetApp.getUi().showModalDialog(html,"Test");
}
function doStuff() {
try {
// let get a value from spreadsheet
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("Sheet1");
return sheet.getRange("A1").getValue();
}
catch(err) {
Logger.log("Error in doStuff() "+err);
}
}
HTML Page HTML_Simple.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input type="button" value="Do Stuff" onClick="doStuffOnClick()">
<input type="text" id="whatStuff">
<script>
function doStuffOnClick() {
try {
google.script.run.withSuccessHandler(
function(response) {
document.getElementById("whatStuff").value = response;
}
).doStuff();
}
catch(err) {
alert(err);
}
}
</script>
</body>
</html>
Reference
HTML Service Best Practices
google.script.run()
I am testing the app script platform and I have a doubt when using this code called from HTML file:
JSON.parse(<?= JSON.stringify(getDataFromSheet("tyreUse", "valueSearched")); ?>);
If I set the string value directly it works.
If I try to pass a variable that is declared in it does not recognize it. How can I pass a JS variable to the app script function like next example?
let value_searched = "cars";
JSON.parse(<?= JSON.stringify(getDataFromSheet("tyreUse", value_searched)); ?>);
Scriptlets like <?= ?> are used in html templates to load data from the server into html pages prior to rendering. If you want to pass data back to a server side function then you can use google.script.run and there are restrictions on the data types that you can pass.
google.script.run
Here is an example of getting data from spreadsheet dynamically. I typically build my page and then use an anonymous function of the form (function () {}()); to get the data from spreadsheet and populate the HTML elements with the values.
Create an HTML file HTML_Demo:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input id="A8SBwf" type="text">
<input id="gNO89b" type="button" value="Click Me" onclick="buttonOnClick()">
<script>
function buttonOnClick() {
try {
google.script.run.withSuccessHandler(
function(response) {
document.getElementById("A8SBwf").value = response;
}
).getCellA1();
}
catch(err) {
alert(err);
}
}
</script>
</body>
</html>
Then in Code.gs create the getCellA1:
function getCellA1() {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var range = sheet.getRange("A1");
return range.getValue();
}
catch(err) {
return err.message;
}
}
I tried solution at I am trying to pass a variable from my Google Script through to HtmlOutputFromFile, but can't get it working. I get error (translated from Dutch): "TypeError: Can't find function createHtmlTemplateFromFile in object HtmlService.
function fncOpenMyDialog() {
//Open a dialog
var htmlDlg = HtmlService.createHtmlTemplateFromFile('DropDown_NewCompetitionFile');
htmlDlg.myVar = "November";
htmlDlg = htmlDlg.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(200)
.setHeight(150);
SpreadsheetApp.getUi()
.showModalDialog(htmlDlg, 'A Title Goes Here');
};
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<select name="nameYouWant">
<option value="something">Text</option>
<option value="anything">Drop Down Selection</option>
<option value="anotherthing"><?myVar?></option>
</select>
<hr/>
<ul>
<li>This is a list.</li>
<li>This is line two.</li>
</ul>
<button onmouseup="closeDia()">Close</button>
<script>
window.closeDia = function() {
google.script.host.close();
};
</script>
</body>
</html>
Replace
HtmlService.createHtmlTemplateFromFile
with
HtmlService.createTemplateFromFile
You can do things any way you wish but this seems a lot simpler:
<input type="button" value="Close" onClick="google.script.host.close()" />
than this:
<button onmouseup="closeDia()">Close</button>
<script>
window.closeDia = function() {
google.script.host.close();
};
</script>
and I prefer to load my select options via javascript on window.onload
I have used createTemplateFromFile many time in the past. It lets me use an include method so that CSS and JavaScript can be broken out into a different file. But today I can't seem to make it work. Below is my test code bound to a spreadsheet. Any ideas why its not working? I tried another existing spreadsheet with a custom dialog using the technique and it works.
Code.gs
function onOpen() {
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu("Test");
menu.addItem("Test", "test");
menu.addToUi();
}
function test() {
try {
var html = HtmlService.createTemplateFromFile("HTML_Test");
Logger.log(html);
// was html.evaluate();
html = html.evaluate(); // correction
SpreadsheetApp.getUi().showSidebar(html);
}
catch(err) {
Logger.log(err);
}
}
HTML_Test:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<select>
<option value="Doc1">Document 1</option>
<option value="Doc2">Document 2</option>
<option value="Doc3">Document 3</option>
</select>
</body>
</html>
Log
[19-01-24 09:54:51:240 PST] {}
[19-01-24 09:54:51:246 PST] Exception: Invalid argument: userInterface
I've been tweeking this every which way to figure out why it wouldn't work. You get blind to a simple solution. html = html.evaluate() works.
I understand why my code does not work, however I can't find a solution searching online. Likely a simple syntax correction. Here is the code:
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getData() {
return SpreadsheetApp
.openById('My Sheet ID')
.getActiveSheet()
.getDataRange()
.getValues();
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
h1 {
width: 100%;
overflow: hidden;
background-color: #ABC;
}
#wrapper {
margin: auto;
width: 60%;
border: 3px solid #000;
padding: 10px;
}
</style>
</head>
<body>
<div id="wrapper">
<h1>Attendace Discrepancy Tracker v1.0</h1>
<h3>Discrepancy date:</h3><input type="date" name="bday" min="2000-01-02"><br>
<? var data = getData(); ?> <------ Gets data from my sheet
<h3>Select a name:</h3><select id='empName' onchange="findEmpID();">
<? for (var i = 1; i < data.length; i++) { ?>
<option value="<?= i ?>"><?= data[i][2] + ", " + data[i][1] + " " + data[i][3]?></option>
<? } ?>
</select>
<div id="empProfile">
<p>Employee Number: <span id="empNumber"></span></p>
<p>Supervisor: <span id="empSup"></span></p>
</div>
</div>
<script>
function findEmpID () {
var e = document.getElementById("empName");
var strUser = e.options[e.selectedIndex].value;
document.getElementById("empNumber")
.innerHTML = <?= data[strUser][0] ?>; <----- This does not work
}
</script>
</body>
</html>
The whole program works, except when I try and reference back to the data array. I need a way to pass the <script> tags strUser value to the data array google script variable so I can get the supervisors ID number. Obviously when I switch to the <? tag space it cannot read variables from the <script> tag. I am sure it is possible, just have not figured out how.
You are trying to use a scriptlet after the HTML has loaded. It looks like you are trying to trigger the scriptlet to run from the onchange attribute of the drop down list. I don't think that scriptlets will run except when the page is first loaded.
I'd change things like this:
Use the this keyword to pass the employee name to the client side findEmpID() function:
<h3>Select a name:</h3><select id='empName' onchange="findEmpID(this.value);">
Note the use
of this.value. Enter a variable name in the function parenthesis to receive the employee name:
function findEmpID(theName) {
Pass the name to the server function:
<script>
function findEmpID(theName) {
google.script.run
.withSuccessHandler(enterName)
.getThisEmployeeID(theName);
};
function enterName(theReturnedName) {
//Put the name into the HTML Element
document.getElementById("empNumber").textContent = theReturnedName;
};
</script>
You'll need to get the data from the spreadsheet, and find the name with the server function:
GS code
function getThisEmployeeID(theName) {
//Get range of data
var arrayOfData = sheet.getRange(parameters).getValues();
//I don't know how the data is configured, but you'll need to match
//the name to the index number in the array, then get the ID
var employeeID = 'xyz';
return employeeID;
};