How to add text to an open Dialog window? - google-apps-script

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()

Related

Apps-script function with dynamic parameter

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;
}
}

App Script - getting the content of a spreedsheet cell and putting it into a text box in a website and clicking the button

I am learning Google App Script and have a question. I am laerning it to do some automation in the future.
Let's assume I have a spreadsheet with cell A1. The content of this cell is "Test"
Let's assume there is a webpage like google. It has a text box and a button(submit button)
I would like to retrieve the content from the cell and put it into the text box. Then I want my script to "click" that button next to it.
Any keyword for me to look for it? Any hint? Am I looking for scrapping or automation? I also would really love to see an example, for example with Google search in order for me to apply it on the actual webpage I am aiming for.
The class name of textbox in Goole Search is class="A8SBwf" and for the button it is class="gNO89b".
Looking forward :))
Here is a quick and dirty example.
In the script editor of your Spreadsheet Code.gs
function onOpen() {
var menu = SpreadsheetApp.getUi().createMenu("Test");
menu.addItem("Dialog","showDialog");
menu.addToUi();
}
// required if you ever expand on a template
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
};
function showDialog() {
try {
var html = HtmlService.createTemplateFromFile('HTML_Demo').evaluate();
SpreadsheetApp.getUi().showModalDialog(html,"Show Demo");
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}
function getCellA1() {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
return sheet.getRange(1,1).getValue();
}
catch(err) {
return err.message;
}
}
Then in the editor create a new 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>
After that you're on your own.

how to transfer textbox value from app inventor to google sheet web app

i have created a google web app,
when i am typing a number in the orders textbox,
i'm getting a match result in the disabled amount textbox,
from the google sheet column,
the web app working good
now,what i want to do is to be able to create a textbox in app inventor
and when put an orders value and clicking a button
i will get the amount result on a label on the app invenotr interface
its not working on app inventor
here is the code i made in app inventor
[![enter image description here][1]][1]
this the Code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('page');
}
function getCost(oneCode){
var url = "https://script.google.com/macros/s/AKfycbyX";
va
return "not found";
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script>
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("one").addEventListener("change", doThis);
});
function doThis() {
var oneCode = document.getElementById("one").value;
google.script.run.withSuccessHandler(updateAmount).getCost(oneCode);
}
function updateAmount(cost) {
document.getElementById("two").value = cost;
}
</script>
<body>
<div>
<input id="one" type="text">
<label for="one">orders</label>
</div>
<div>
<input disabled id="two" type="text">
<label for="two">amount</label>
</div>
</body>
</html>
Try this:
function doThis() {
var oneCode = document.getElementById("one").value;
console.log(oneCode);
google.script.run.withSuccessHandler(updateAmount).getCost(oneCode);
}
and this:
function getCost(oneCode){
console.log('onecode');
var url = "https://script.google.com/macros/s/AKfycbyXX6axO9G9ANDW0LaQVgezT6hSzSld8CRt2VbsZV6v8XXwNus/exec";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Sheet1");
var data = ws.getRange(1,1,ws.getLastRow(),2).getValues();
var ordersList = data.map(function(r){ return r[0].toString(); });
var amountList = data.map(function(r){ return r[1].toString(); });
var position = ordersList.indexOf(oneCode);
if(position > -1){
return amountList[position];
}
return "not found";
}
And then run the code and goto View/Executions and see where you losing the data.

Direct to a new page after form submission

Currently I've developed a Google Scripts API which is used for people to upload files to a shared Google Drive folder. After the file are uploaded successfully, I want them to be taken to a separate "Thank you" page so it is clear their upload has worked. Currently I only have a message on the same page to this effect and I cannot figure out how to direct to a new page that I have created.
This is the additional bit I found from different questions to try direct to a new page however this is not working so far, as it remains on the same upload form page. I have included it at the bottom of my code.gs file. Any ideas on how to direct to a custom page that just says "thank you" or something similar would be great!
function doPost(e) {
var template = HtmlService.createTemplateFromFile('Thanks.html');
return template.evaluate();
}
The rest of my code is as follows:
Code.gs:
function doGet() {
return HtmlService.createHtmlOutputFromFile('form').setSandboxMode(
HtmlService.SandboxMode.IFRAME);
}
function createFolder(parentFolderId, folderName) {
try {
var parentFolder = DriveApp.getFolderById(parentFolderId);
var folders = parentFolder.getFoldersByName(folderName);
var folder;
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = parentFolder.createFolder(folderName);
}
return {
'folderId' : folder.getId()
}
} catch (e) {
return {
'error' : e.toString()
}
}
}
function uploadFile(base64Data, fileName, folderId) {
try {
var splitBase = base64Data.split(','), type = splitBase[0].split(';')[0]
.replace('data:', '');
var byteCharacters = Utilities.base64Decode(splitBase[1]);
var ss = Utilities.newBlob(byteCharacters, type);
ss.setName(fileName);
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFilesByName(fileName);
var file;
while (files.hasNext()) {
// delete existing files with the same name.
file = files.next();
folder.removeFile(file);
}
file = folder.createFile(ss);
return {
'folderId' : folderId,
'fileName' : file.getName()
};
} catch (e) {
return {
'error' : e.toString()
};
}
}
function doPost(e) {
var template = HtmlService.createTemplateFromFile('Thanks.html');
return template.evaluate();
}
Form.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div align="center">
<p><img src="https://drive.google.com/uc?export=download&id=0B1jx5BFambfiWDk1N1hoQnR5MGNELWRIM0YwZGVZNzRXcWZR"
height="140" width="400" ></p>
<div>
<form id="uploaderForm">
<label for="uploaderForm"> <b> Welcome to the Tesco's animal welfare and soy reporting system. </b> </label>
<BR>
<BR>
<div style="max-width:500px; word-wrap:break-word;">
Please add your contact information below and attach a copy of your company's animal welfare standard before clicking submit. Wait for the browser to confirm your submission and you may then close this page.
<BR>
<BR>
Thank you very much for your submission.
</div>
<BR>
<BR>
<div>
<input type="text" name="applicantName" id="applicantName"
placeholder="Your Name">
</div>
<BR>
<div>
<input type="text" name="applicantEmail" id="applicantEmail"
placeholder="Your Company">
</div>
<BR>
<BR>
<div>
<input type="file" name="filesToUpload" id="filesToUpload" multiple>
<input type="button" value="Submit" onclick="uploadFiles()">
</div>
</form>
<br>
<br>
<br>
<br>
<br>
<br>
<div id="output"></div>
<script>
var rootFolderId = '1-aYYuTczQzJpLQM3mEgOkWsibTak7KE_';
var numUploads = {};
numUploads.done = 0;
numUploads.total = 0;
// Upload the files into a folder in drive
// This is set to send them all to one folder (specificed in the .gs file)
function uploadFiles() {
var allFiles = document.getElementById('filesToUpload').files;
var applicantName = document.getElementById('applicantName').value;
if (!applicantName) {
window.alert('Missing applicant name!');
}
var applicantEmail = document.getElementById('applicantEmail').value;
if (!applicantEmail) {
window.alert('Missing applicant email!');
}
var folderName = applicantEmail;
if (allFiles.length == 0) {
window.alert('No file selected!');
} else {
numUploads.total = allFiles.length;
google.script.run.withSuccessHandler(function(r) {
// send files after the folder is created...
for (var i = 0; i < allFiles.length; i++) {
// Send each file at a time
uploadFile(allFiles[i], r.folderId);
}
}).createFolder(rootFolderId, folderName);
}
}
function uploadFile(file, folderId) {
var reader = new FileReader();
reader.onload = function(e) {
var content = reader.result;
document.getElementById('output').innerHTML = 'uploading '
+ file.name + '...';
//window.alert('uploading ' + file.name + '...');
google.script.run.withSuccessHandler(onFileUploaded)
.uploadFile(content, file.name, folderId);
}
reader.readAsDataURL(file);
}
function onFileUploaded(r) {
numUploads.done++;
document.getElementById('output').innerHTML = 'uploaded '
+ r.fileName + ' (' + numUploads.done + '/'
+ numUploads.total + ' files).';
if (numUploads.done == numUploads.total) {
document.getElementById('output').innerHTML = 'All of the '
+ numUploads.total + ' files are uploaded';
numUploads.done = 0;
}
}
</script>
<label for="uploaderForm">
Powered by 3Keel
</label>
</body>
</html>
Thanks.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Thank you for submitting!
</body>
</html>
EDIT:
I have changed this function as recommended:
if (numUploads.done == numUploads.total) {
window.location = 'Thanks.html';
numUploads.done = 0;
Now it is redirecting to another page but I am faced with this error:
That’s an error.
The requested URL was not found on this server. That’s all we know.
If you are looking for the solution of your issue yet, how about this answer?
You want to open Thanks.html when the process at Form.html is finished.
Form.html and Thanks.html are put in a project.
If my understanding is correct, how about this workaround? I have ever experienced with your situation. At that time, I could resolve this issue by this workaround.
Modification points:
It is not required to use doPost() to access to Thanks.html. I think that you can achieve what you want using doGet().
I think that #Scott Craig's answer can be also used for this situation. In my workaround, the URL of window.location = 'Thanks.html'; is modified.
Uses the URL of deployed Web Apps. In your script, when users access to your form, they access to the URL of the deployed Web Apps. In this workaround, it is used by adding a query parameter.
Modified scripts:
Form.html
For the script added in your question as "EDIT", please modify as follows.
From:
window.location = 'Thanks.html';
To:
window.location = 'https://script.google.com/macros/s/#####/exec?toThanks=true';
https://script.google.com/macros/s/#####/exec is the URL of the the deployed Web Apps. Please add a query parameter like toThanks=true. This is a sample query parameter.
Code.gs
Please modify doGet() as follows.
From:
function doGet() {
return HtmlService.createHtmlOutputFromFile('form')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
To:
function doGet(e) {
if (e.parameter.toThanks) {
return HtmlService.createHtmlOutputFromFile('Thanks')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
} else {
return HtmlService.createHtmlOutputFromFile('form')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
}
Note:
When the script of the deployed Web Apps was modified, please redeploy it as new version. By this, the latest script is reflected to Web Apps.
The flow of this modified script is as follows.
When users access to the Form.html, because the query parameter of toThanks=true is not used, Form.html is returned.
When onFileUploaded() is run and if (numUploads.done == numUploads.total) {} is true, it opens the Web Apps URL with the query parameter of toThanks=true. By this, if (e.parameter.toThanks) {} of doGet() is true, and Thanks.html is returned and opened it.
In my environment, I could confirm that this modified script worked. But if this didn't work in your environment, I'm sorry. At that time, I would like to think of about the issue.
I might be misunderstanding your question, but from what I understand, instead of this line:
document.getElementById('output').innerHTML = 'All of the '
+ numUploads.total + ' files are uploaded';
You want to redirect to Thanks.html. If that's correct, just replace the above line with:
window.location = 'Thanks.html';

Holding value in textarea for Google Script

I'm trying to write code that will help some of my users take a JSON response and convert it to a table in Google Sheets. I have the code for the JSON to Table script, courtesy of Amit Agarwal at www.ctrlq.org.
The way that I'm trying to do is that Google Sheets will generate a pop up for my user to copy and paste the JSON straight into it and then it will pass the object to the JSON to Table code. I'm having a problem figuring out how to actually do that.
Code.gs
function showPrompt()
{
var ui = SpreadsheetApp.getUi(); // Same variations.
var html = HtmlService.createHtmlOutputFromFile('Upload');
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Dialog title');
}
function setJSON(json)
{
var json = json; //json object?
return json;
}
// Written by Amit Agarwal www.ctrlq.org
function writeJSONtoSheet(json) {
var sheet = SpreadsheetApp.getActiveSheet()[1];
var keys = Object.keys(json).sort();
var last = sheet.getLastColumn();
var header = sheet.getRange(1, 1, 1, last).getValues()[0];
var newCols = [];
for (var k = 0; k < keys.length; k++) {
if (header.indexOf(keys[k]) === -1) {
newCols.push(keys[k]);
}
}
if (newCols.length > 0) {
sheet.insertColumnsAfter(last, newCols.length);
sheet.getRange(1, last + 1, 1, newCols.length).setValues([newCols]);
header = header.concat(newCols);
}
var row = [];
for (var h = 0; h < header.length; h++) {
row.push(header[h] in json ? json[header[h]] : "");
}
sheet.appendRow(row);
}
Dialog box HTML:
<!DOCTYPE html>
<html>
<head>
<script>
function setJSON() {
// how do i add value to set to set JSON
var json = ""
google.script.run.setJSON(json);
}
</script>
<base target="_top">
</head>
<body>
text text
<textarea rows="20" cols="20" placeholder="Paste your Data here. Do not format. Do not worry if it looks weird." name="json">DATA</textarea>
<input type="button" class="button" value="Submit JSON" onclick="setJSON()">
</body>
</html>
Thank you in advance!
Here's a simple example of triggering a dialog via an in-sheet menu and passing the input back to a server side function.
Code.gs
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Input Form Test')
.addItem('Input Form', 'showInputForm')
.addToUi();
}
function showInputForm() {
var html = HtmlService.createHtmlOutputFromFile('inputform');
SpreadsheetApp.getUi().showModalDialog(html, 'Input Form Test');
}
function logInput(input) {
Logger.log(input)
}
inputform.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script>
function setJSON(form) {
var jsonData = document.getElementById('json-data');
google.script.run.logInput(jsonData.value);
google.script.host.close();
}
</script>
<body>
<textarea id="json-data" rows="20" cols="20" placeholder="Paste your data here. Do not format. Do not worry if it looks weird." name="json">DATA</textarea>
<input type="button" class="button" value="Submit JSON" onclick="setJSON()">
</body>
</html>
Logging Output
After clicking the "Submit JSON" button with "test input" entered into the form, this is the output in the script logs:
[18-06-15 13:47:15:741 PDT] test input
Notes
I added an id attribute to the textarea element
the id is used to fetch the element programmatically
you may want to perform some client-side validations in setJSON(). For example:
verify that the value is not still the default value (i.e. DATA)
validate that the JSON data can be parsed succesfully (i.e. JSON.parse())
an example implementation with the above validations:
function setJSON(form) {
var jsonData = document.getElementById('json-data');
if (jsonData.value == 'DATA') {
alert("Please enter JSON data into the form.");
return;
}
try {
JSON.parse(jsonData.value)
}
catch(e) {
console.log(e); // Outputs to the browser console.
alert("Unable to parse JSON data.");
return;
}
google.script.run.logInput(jsonData.value);
google.script.host.close();
}
to fit your use case, in inputform.html you'd replace the logInput() call with either:
a function that calls JSON.parse() and then calls writeJSONtoSheet() with the result
writeJSONtoSheet() if you update it to expect text and add a JSON.parse() call at the beginning of the function