I'm trying to get the customer to enter the ID of their spreadsheet, so that the remaining script performs the proper functions on the customer's spreadsheet. My code is currently like this:
Script.gs
var id;
var spreadsheet=SpreadsheetApp.openById(id);
function myfunction(x) {
id=x;
}
function doGet() {
var template = HtmlService.createTemplateFromFile('HTML')
var html=template.evaluate();
return html
}
HTML:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input>ID></input>
<button onclick="passId()">PASS</button>
<script>
function passId() {
google.script.run.myfunction(id);
}
</script>
</body>
</html>
When I try to view the HTML, it returns an error saying that the value of the ID variable is invalid. The intention is for the client to inform the ID of his worksheet, as the rest of the code depends on the ID of the worksheet to work. If I assign the id manually through .gs, the HTML works perfectly in the worksheet with the id assigned, thus applying all the functions present in my code.
EDIT
I made some changes according to the answers, and it looks like this
GS
function logUserInput(x){
var id=x;
Logger.log(id);
return id;
}
function doGet(e) {
var template = HtmlService.createTemplateFromFile('HTML');
template.variablename = "";
var html=template.evaluate();
return html;
}
HTML
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input id="KK" type="text" value="<?= variablename ?>"/>
<button onclick="passId()">PASS</button>
<script>
function passId() {
google.script.run.logUserInput(document.getElementById("KK").value);
}
</script>
</body>
</html>
Now I will try to assign the value that is inside the "logUserInput(x)" function to a global variable. Thanks!
In Google Apps Script, stuff set out of any function, the global scope, is executed every time that a server side function, including doGet, is ran from the editor, a user interface, including a web application, or by simple/installable triggers.
So first thing that you should do is to remove the following lines from the global scope.
var id;
var spreadsheet=SpreadsheetApp.openById(id);
Below is a simplified implementation of a web app that pass a value entered in a web application created using Google Apps Script to the server side code. Please checkout the comments.
You might try this code or adapt it to your current code.
/**
* Creates the web application using an .html file
*/
function doGet(e){
return HtmlService.createHtmlOutputFromFile('index');
}
/**
* Function called from the client-side. It logs the value
* entered by the web-app user.
*/
function logUserInput(id){
console.log(id);
return `${id} successfully logged`;
}
index.html Contains the web application structure and client-side JavaScript
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<label for="my-input">Enter the spreadsheet id</label><br>
<input id="my-input" type="text"><br>
<button onclick="sendToServer">Save</button>
<script>
/**
* Called by the button click event.
* Sends the input value to the server side and logs the server side
* returned string or the error message if it occurs on the server side
*/
function sendToServer(){
/** Retrieves the input value */
const id = document.querySelector('input').value;
/** Send the input value to the server */
google.script.run
.withSuccessHandler(console.log)
.withFailureHandler(console.error)
.logUserInput(id);
}
</script>
</body>
</html>
References
https://developers.google.com/apps-script/guides/web
https://developers.google.com/apps-script/guides/html/communication
Related
Here is Docusign's Clickwrap example.
var myCustomUserId = Math.floor(Math.random() * 100000) + 1;
console.log(myCustomUserId); // this works
<div id="ds-terms-of-service"></div>
<script src="https://demo.docusign.net/clickapi/sdk/latest/docusign-click.js"></script>
<script>docuSignClick.Clickwrap.render({
environment: 'https://demo.docusign.net',
accountId: 'daf5048a-xxxx-xxxx-xxxx-d5ae2a842017',
clickwrapId: '9888ca17-xxxx-xxxx-xxxx-9bd95f46345d',
clientUserId: myCustomUserId
}, '#ds-terms-of-service');
I am creating an overly simple html app that is attempting to send a custom userid to the Clickwrap service. However, the above example does not work (even using our actual accountId and clickwrapId). How do you update myCustomUserId in this clickwrap call with a variable inside my html (hopefully using simple javascript/css/html)?
I have a hunch the example code I posted above (copied from their website) was meant as a 1-off test and not a fully functioning example. I want to confirm my suspicion that we may have to call another api to request a clickwrap, and pass that api the clientUserId, and in return, docusign returns the full clickwrap with the embedded clientUserId value.
Thanks in advance!
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div id="ds-clickwrap"></div>
<script src="https://demo.docusign.net/clickapi/sdk/latest/docusign-click.js"></script>
<script>
const myCustomUserId = Math.floor(Math.random() * 100000) + 1;
docuSignClick.Clickwrap.render(
{
environment: "https://demo.docusign.net",
accountId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
clickwrapId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
clientUserId: `${myCustomUserId}`
},
"#ds-clickwrap"
);
</script>
<script src="index.js" async defer></script>
</body>
</html>
Hey zenbudda! For testing purposes, This should do the trick. You need to pass in your custom ID properly.
I create function get gmail when user use my app in file gs. When i call function via file script html it run but don't get gmail ?
I try test open new web browsers, log in with account gmail and pass URL of apps script but not show my gmail.
Code in file .gs:
function checkAccSS(){
var email = Session.getActiveUser().getEmail();
Logger.log(email); // I test it show my gmail
return email;
}
Code in file .html:
<!DOCTYPE html>
<html>
<head>
</head>
<body onload="doLoadGmail()">
<p id="demo"></p>
<script>
function doLoadGmail(){
google.script.run.withSuccessHandler(checkGmall).checkAccSS();
}
function checkGmall(email){
document.getElementById("demo").innerHTML = "Gmail " + email;// not show gmail in html !
console.log(info); // not show gmail here !
}
</script>
</body>
</html>
As far as I can see this happens because your script 'doLoadGmail()' that executes the script 'checkAccSS' is only called on load. You could add a button with an onclick event that would execute your script without reloading however this wouldn't do it automaticly.
If you would like to this automaticly you could check the documentation from Ajax
https://www.w3schools.com/jquery/jquery_ref_ajax.asp
I'm trying to put some content in my automated reply messages from Google Apps Script. My project has a AutomatedResponseTemplate.html the script hits for some response HTML string to send. That file is defined like this :
<!DOCTYPE html>
<html>
<head>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.1/mustache.js></script>
<script>
$(function() {
let data = <?= JSON.stringify(messageData) ?>
// make request to "server"
template = $('body').html()
rendering= Mustache.render(template, data)
$('body').html(template)
})
</script>
<style>
#closing {
white-space: pre-line;
}
.separate-lines {
white-space: pre-line;
}
.first {
width: 80px;
}
.left {
float: left;
}
</style>
</head>
<body>
<p>
{{name}}
</p>
<p>
Thank you for reaching out to us! This is an automated reply, and one of us shall reach out to you,
for real, shortly. Here is a copy of the message you sent us for reference:
</p>
<p>
<blockquote class=separate-lines>
<span><label class="first left">Sender:</label> {{sender}}</span>
<span><label class="first left">Subject:</label> {{subject}}</span>
<span><label class="first left">Message:</label> {{message}}</span>
</blockquote>
</p>
<p id=closing>
Mike Warren
Open Source Roads
</p>
</body>
</html>
To separate concerns, and keep things RESTful, I have the script send the relevant data to the HTML via that tag. There's a working pure-client-side version prior and I got the idea to do it that way from thinking about it and the implementation here.
There's one problem: it doesn't work. Even if I append some non-template code
$('#data').text('This was inserted by jQuery')
and a tag
<p id=data></p>
...nothing changes.
What in the world is going on?
UPDATE
I updated the <script> tag to this:
<script>
$(function() {
let data = <?!= JSON.stringify(messageData.data) ?>
// make request to "server"
template = $('body').html()
rendering= Mustache.render(template, data)
$('body').html(rendering)
$('#data').text('This text was inserted by jQuery')
})
</script>
, enclosed the src values on the client-side dependencies with quotes (idk why that matters to Google Apps Script, as it works fine elsewhere), and provided a doGet for debugging purposes:
function doGet(e) {
var messageData = {
data: {
sender: 'mwarren04011990#gmail.com',
name: 'Test User',
recipient: Session.getActiveUser().getEmail(),
subject: 'Test email',
message: 'Hello world'
}
}
var template = HtmlService
.createTemplateFromFile('AutomatedResponseTemplate')
template.messageData = messageData
return template
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
}
Also, the function that is supposed to get this client side template rendering as a string has this code:
/**
* Generates an email message body in response to the sender's `messageData`
* #param {object} messageData - a JSON object with at least the following:
* - `sender`
* - `name`
* - `subject`
* - `message`
*/
function getAutomatedResponseTo(messageData) {
messageData = messageData || {};
if (!messageData.sender) return '';
if (!messageData.name) return '';
if (!messageData.subject) return '';
if (!messageData.message) return '';
var template = HtmlService
.createTemplateFromFile('AutomatedResponseTemplate')
template.messageData = messageData;
return template
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.getContent()
}
Is my trying to separate the concerns this way infeasible?
I have replicated your error. If you look at the developer tools in chrome you will see the following error:
This means that jQuery is not available.
If you look at the network tab in developer tools you will see that the attempt to load jquery has failed and the url is badly mangled:
The solution is to put quotes around the resources you are loading using the script tag:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.1/mustache.js"></script>
When that is done the dialog seems to work fine and the Mustache templates are populated correctly. Some browsers can live without the quotes but it seems that Google Apps Script cannot.
I think you are missing an !. This is the code that I use:
<script>
var data = <?!= JSON.stringify(dataFromServerTemplate) ?>;
</script>
you can do a console.log(data) and look at it in developer tools to see what you get.
I just went ahead and made Google Apps Script take care of all the templating. (Sorry, Mustache.js, but you didn't seem to be working without a browser open to show your work!)
I took out the <script> tags, and replaced the mustaches with the Google Script template tags containing the data fields directly. For example, I replaced {{name}} with <?= messageData.name ?>.
Good morning and thank you in advance for the help. I am a noobi at java/gas scripting so any help is appreciated.
On a google spreadsheet I have a custom menu that launches a small html menu that I would like to be able to launch various web pages from. The actual addresses are variable. I have set those as Keys with the property service when the page launches.
Here is the html code (taken from another example and trying to adapt)"
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<script type="text/javascript">
var myArr = ["Peter", "Paul", "Tony", "Adam"];
function display(name) {
var accScriptPageLinkID= PropertiesService.getScriptProperties().getProperty('scriptPageLinkAdd');
Logger.log(accScriptPageLinkID)
alert(name);
}
for(var i = 0; i < 4; i ++) {
document.write('<input type="button" onclick="display(this)" name="'+ myArr[i] + '" value="'+ myArr[i] +'">'); // Will log Peter, Paul..
}
</script>
</body>
</html>
I need to modify the above code so that when the button Peter is pressed it opens the Script Page linked under the Property Service Key 'scriptPageLinkAdd'.
Or if there is an easier way to create a dynamic html page for my menu that is linked to cells in the google spreadsheet, please advise.
Mike
PropertiesService (and Logger too) are server-side AppsScript classes - they can't be invoked directly from the client side (your html page).
To invoke them on the server side from your html page you must use google.script.run.withSuccessHandler(clientSideFunctionToProcessReturnedData).someServerSideFunction() which can return some data back to your html page.
Learn more about HtmlService and communicating with the server here
I have tried out a scenario which I'm confused of. This is may be javascript 101, but I'm missing a point here.
Assume you have JS library which has a global variable defined. Also it has two functions called setData and retunData .I have separate html with a script within it's Head tag. This HTML file imports above library.
Now on the html I have a text field with two buttons.Buttons are send data and view data. The flow is, user type something and click send Data button. On its onClick I'm getting the text value and pass it to above JS library setData function which sets data to global variable. When i click the view data , on it's onClick it will call the returnData functio of JS Library and I'm alerting the value.
My question is, if I run my html on two browsers , and from one browser if i set the value "ONE" , on the same browser when i retrieves it says "ONE". On the second browser if returned the value it should be "ONE" because that variable is global and value is already set by first browser.
But it works as two requests. it won't override the global variable value. I know it should be the ideal case. But WHY? That's a global variable right? I hope my question is clear.
on the jsLibrary i have this.
var checkVal;
function setData(val){
checkVal = val;
}
function viewData(){
return checkVal;
}
My html is this.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="js/jsLibrary.js"></script>
<script src="js/jquery-2.1.1.min.js"></script>
<title></title>
<script type="application/javascript">
function sendData(){
var val = $("#idTextVal").val();
setData(val);
}
function viewData(){
alert(returnData());
}
</script>
</head>
<body>
<input type="text" id="idTextVal">
<input type="button" id="idConnect" onclick="sendData()" value="SEND">
<input type="button" id="idChecj" onclick="viewData()" value="VIEW">
</body>
</html>
WHY? That's a global variable right?
It is global, but for this particular tab/page instance. If you open that same page in other tab, javascript engine will initialize all variables, because it doesn't know that you have other tab.
In your case you have multiple options. Perhaps, the most straightforward is to use serverside storage to preserve state. Or you can use simple cookies/localStorage approach, they are also shared by all pages on the same origin. I would go with WebStorage as simpler:
function setData(val) {
localStorage.checkVal = val;
}
function viewData() {
return localStorage.checkVal;
}