Here, the task is to use a variable from Code.gs to be used in the HTML side.
The best idea I've had is using google.script.run to get access to Code.gs where I have stored a variable that I wish to use in the HTML script. Eg: Suppose there is a variable in the Code.gs side that turned out to be 1+1. Then I would very much have liked the following to work:
Code.gs
function getMyGSValue() {
return 1+1
}
HTML
<script>
google.script.run.withSuccessHandler(myGsValue => {
myScriptVar = myGsValue
}).getMyGsValue()
// Here use MyScriptVar endlessly.
</script>
Which unfortunately fails to work. If it's of any help, I'm more interested in using string variables from the Code.gs side as these will be more likely the link to the images I want to display if particular conditions are met.
A related question follows:
Passing variable from Code.gs to html in Google App Script
But to be honest, It seemed to have its focus elsewhere.
Doing it with google.script.run. I just used window.onload event to get the data from the server
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input type="text" id="txt1" />
<script>
window.onload = () =>{
//console.log("window.onload")
google.script.run.withSuccessHandler((v) => {
document.getElementById("txt1").value = v;
}).getMyGsValue()
//console.log("Code");
}
</script>
</body>
</html>
gs:
function getMyGsValue() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
return sh.getRange("A1").getValue();
}
function launchmydialog() {
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutputFromFile("ah2"),"Title");
}
If you need that the "variable" from the server side be ready when the web browser start parsing the web application, instead of using client-side code to retrieve the "variable", generate the client-side code with the "variable" that you need by generating first a HtmlTemplate, then assigns the variable to a HtmlTemplate property and finally evaluates it.
Below is an over simplistic example:
function doGet(e){
const myServerVariable = 1 + 1;
const template = `<body><script>const myClientVariable = <?!= tmpProp?> </script></body>`;
return (
HtmlService.createTemplate(template)
.tmpProp = myServerVariable
).evaluate();
}
The above example is so simple that you could do the same by using regular JavaScript string manipulation. Anyway, <?!= tmpProp?> is a force-printing scriptlet, also there are standard scriptlets and printing scriptlets that might be more frequently used on .html files in Google Apps Script.
Be careful when using scriptlets to built the client-side code to not make them to take too much time to generate the client-side code as this will impact how fast the web app responds to the initial request.
By the other hand, if you want to keep using google.script.run, just declare a global variable, and update it in the withSuccessHandler callback
<script>
var MyScriptVar;
google.script.run.withSuccessHandler(myGsValue => {
MyScriptVar = myGsValue
}).getMyGsValue()
// Here use MyScriptVar endlessly.
</script>
Just consider the case that MyScriptVar will be undefined while the google.script.run finish it's execution.
Related
How to pass a parameter to html?
Passing variable from google script to html dialog
References
https://developers.google.com/apps-script/guides/html/templates
Related
I want to fetch the title I set using setTitle method of HtmlOutput within the html source file, i.e., client-side, but it seems that calling document.title returns me empty string when I call it within my html file.
Main.gs:
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'Build Form...', functionName: 'buildForm_'},
];
spreadsheet.addMenu('Test', menuItems);
}
function buildForm_(){
const reportTitle = "Some Title"
const htmlForm = HtmlService.createHtmlOutputFromFile('HTML_Sidebar')
.setTitle(reportTitle);
SpreadsheetApp.getUi()
.showSidebar(htmlForm)
}
HTML_Sidebar.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button onmouseup="seeTitle()" id="seeTitleButton">See title</button>
<script>
function seeTitle() {
document.getElementById("seeTitleButton").textContent = "(" + document.title + ")"
}
</script>
</body>
</html>
Google Apps Script's documentation doesn't seem to specify a client-side method in the HTML Service to query the title, and I was hoping that someone in the community could point me towards the right direction.
The issue is that <title> is assigned to a outer iframe than the one your userAppCode lives in (userHtmlFrame) so when you call the document node you are reading from the iframe your app is contained in, which does not have the title.
A work around would be to use the PropertiesService and store the title as a script property when setting it. And retrieving it from the properties when you want to use it. You still would need to use google.script.run to call a function on the server-side which would fetch the title and return it to the client.
Use getTitle():
Gets the title of the output page. Note that the <title> HTML element is ignored.
var output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');
Logger.log(output.getTitle());
Source: https://developers.google.com/apps-script/reference/html/html-output#gettitle
I am new to using Google script, but not completely new to programming.
I have looked at some examples and they generate HTML output like this:
function doGet(e) {
return HtmlService.createHtmloutputFromFile('form.html')
}
I would like to pass a parameter to my script to use that in the output.
I have gotten as far as that I can use this in the function:
var room= e.parameter.room
So when I execute my script with url?room=test
I do get the value for that parameter.
But how can I use that variable room that I create in the HTML output and other code?
I have been looking at createTemplateFromFile but I am not getting anywhere.
Hope someone can point me in the right direction of what constructs and command to look at.
You can directly get query parameters client side1:
form.html
<script>
google.script.url.getLocation(function(location) {
alert(location.parameter.room); //alerts "test" on loading "url?room=test"
});
</script>
Alternatively, You can use scriplets to load html2:
code.gs
function doGet(e) {
var temp = HtmlService.createTemplateFromFile('form');
temp.room = e.parameter.room;
return temp.evaluate();
}
form.html:
<script>
alert('<?=room?>'); //Printing scriplets
</script>
I want to use app script in my Google form to automatically submit the form in 20 minutes if the user doesn't click on submit within 20 minutes. Anyway to implement this????
No, you cannot control the client-side of Google Forms, even if you add an Apps Script to it, because Apps Script runs on the server.
One possible solution is to serve your form as a Google Apps Script web app. At that point you can write client-side JavaScript and use window.setTimeout to submit the form after 20 minutes.
Here are some example files, Code.gs and quiz.html, that can provide a basic skeleton to start the web app. A blank project will have Code.gs as the default file, then you have to add File > New > HTML file to start the other file.
You can enter the id of any spreadsheet you own in the commented out lines in Code.gs to append the response into that spreadsheet. (You can also automate that process by creating a new spreadsheet as needed. Example of creating spreadsheet to hold data for Apps Script example can be found here.
// file Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("quiz");
}
function doPost(request) {
if (request.answer) {
console.log(request.answer); // View > Execution transcript to verify this
//var ss = SpreadsheetApp.openById(id).getSheetByName("Quiz Responses");
//ss.appendRow([request.answer /* additional values comma separated here */ ]);
}
}
<!DOCTYPE html>
<!-- file quiz.html -->
<html>
<head>
<base target="_top">
</head>
<body>
<h1>Quiz</h1>
<form>
What is Lorem Ipsum?
<input name="loremipsum" type="text"/>
<button>Submit</button>
</form>
<script>
const button = document.querySelector("button");
const timeLimitMinutes = 1; // low number for demo; change to 20 for application
const timeLimitMilliseconds = timeLimitMinutes * 60 * 1000;
// For this demo we are not going to serve a response page, so don't try to.
button.addEventListener("submit", submitEvent => submitEvent.preventDefault());
// attach our custom submit to both the button and to the timeout
button.addEventListener("click", submitForm)
window.setTimeout(submitForm, timeLimitMilliseconds)
function submitForm() {
button.setAttribute("disabled", true);
document.querySelector("h1").textContent = "Quiz submitted";
// for demo: submitting just a single answer.
// research Apps Script documentation for rules on submitting forms, certain values not allowed
// consider a helper function `makeForm()` that returns a safe object to submit.
const answer = document.querySelector("input").value;
google.script.run.doPost({ answer });
}
</script>
</body>
</html>
Test with Publish > Deploy as web app...
Let's say we have a variable. This variable was created in google app script. On that app script project, you have two files. First, the .gs file, where the variable came from. Next, you have the html file. How do you transfer the variable to html?
GAS:
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function items() {
var exmp = 45;
document.getElementById("test").innerHTML = "You have " + exmp + " items";
HTML:
<script>
google.script.run.items();
</script>
<div id="test"></div>
However, this doesn't work. How can I make this work?
If you read over the Private Functions section of the HTML service documentation, you'll find an example that does almost exactly what you're trying. The code below adapts that example to yours.
You need to keep the GAS server stuff separate from the HTML client stuff. For example, document.getElementById("test").innerHTML = ... means nothing in the context of the server / GAS code. Instead, the modification of the document will be done by Javascript on the client side - in this case, by a success handler.
A success handler is a client-side Javascript callback function that will receive the asynchronous response from your server function items().
Client-side calls to server-side functions are asynchronous: after the
browser requests that the server run the function doSomething(), the
browser continues immediately to the next line of code without waiting
for a response.
This means that there is no waiting for the return code from the call to your server function... the browser just keeps going. You'll see this in this example, as the "More loading..." text gets displayed after the google.script.run call, but before the response is received.
What if items() needs to do something more advanced... like read info from a spreadsheet? Go ahead and change it... just make sure that you return the text you want displayed, and that what you're returning is going to be valid HTML (so the innerHTML operation is OK).
Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function items() {
Utilities.sleep(5000); // Added to allow time to see the div content change
var exmp = 45;
return( "You have " + exmp + " items" );
}
index.html
<div id="test">Loading...</div>
<script type="text/javascript">
function onSuccess(items) {
document.getElementById('test').innerHTML = items;
}
google.script.run.withSuccessHandler(onSuccess).items();
document.getElementById('test').innerHTML = "More loading...";
</script>
You need first to create the HTML using the createHTMLOutput function
In order for you to append strings youo have to use the var.append method
function items(){
var email = Session.getActiveUser().getEmail();
var output = HtmlService.createHtmlOutput(email);
var string1 = HtmlService.createHtmlOutput('<p>You have </p>')
string1.append(output);
string2= HtmlService.createHtmlOutput('<p> items</p>')
string1.append(string2);
Logger.log(string1.getContent());
}
Reference
I have a UiApp that creates some form elements that write data to a specific spreadsheet. Now I want to load the UiApp widgets into an htmlService template, and I'm not sure how to go about this.
My doGet function loads the base template, and the doGetApp builds the UiApp:
function doGet() {
return HtmlService.createTemplateFromFile('base_template').evaluate();
}
function doGetApp() {
// creates input forms for a spreadsheet (the following is just an example
var app = UiApp.createApplication();
var tabPanel = app.createDecoratedTabPanel();
var flowPanel1 = app.createFlowPanel();
var flowPanel2 = app.createFlowPanel();
tabPanel.add(flowPanel1, "Create New Projects");
tabPanel.add(flowPanel2, "Edit Projects");
app.add(tabPanel);
// return app; // <<<< original doGet function returned the app
// testing different ways of returning the UiApp
return HtmlService.createHtmlOutput(app); // this displays the text "HtmlOutput"
}
My base_html template is very simple right now:
<html >
<?!= getCSS("css_js"); ?>
<body>
<h1>Template Test</h1>
<?!= doGetApp(); ?>
</body>
</html>
I've tried a few variations on this, but the results are generally the same: Text output that appears to describe the object, as opposed to displaying the UiApp. (the getCSS function works fine)
The goal is to be able to easily load some of my own css / js as well as the UiApp in order to easily style the forms / resulting content. I'm pretty new to this, so it is entirely possible that I'm approaching this the wrong way. Appreciate any pointers. Thanks!
-greg
For now, you either use html service or uiapp - you cannot combine the two