Google Apps Scripts - server-side function calling - html

I run the doGet() function. It creates a modal dialog on a spreadsheet. It will show a "Close" and "Make Copy" button where the latter will run a server-side function, doSomething(), that makes a copy of a template. Regardless of whether I attach the function to a button or run it straight in a script tag, it refuses to run. Is there anyway to fix or at the least debug this?
Code.gs
function doGet() {
return SpreadsheetApp.getUi().showModalDialog(HtmlService.createHtmlOutputFromFile('Index'), 'Report');
}
function doSomething() {
var file = template.makeCopy();
file.setName('NEW FILE NAME')
google.script.host.close()
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
google.script.run.doSomething()
</script>
</head>
<body>
<input type="button" value="Close" onclick="google.script.host.close()"/>
<input type="button" value="Make Copy" onclick="google.script.run.doSomething();" />
</body>
</html>

doGet() is a reserved word for web apps.
SpreadsheetApp.getUi only could be used on bounded projects
google.script.host.close() is a client-side method that only works for dialogs and sidebars not for web apps.
Suggestions:
As your project is a bounded project,
change the name of the doGet() function.
Remove google.script.host.close() from doSomething()
Remove
<script>
google.script.run.doSomething()
</script>
Once you make the above changes add menu to call your renamed function. If still doesn't work look for errors at the browser console for client-side errors and to Stackdriver logs for server-side errors.
Quotes
doGet(e) runs when a user visits a web app or a program sends an HTTP GET request to a web app.
google.script.host is an asynchronous client-side JavaScript API that can interact with dialogs or sidebars in Google Docs, Sheets, or Forms that contain HTML-service pages. To execute server-side functions from client-side code, use google.script.run. For more information, see the guide to communicating with server functions in HTML service.
References
https://developers.google.com/apps-script/guides/triggers/
https://developers.google.com/apps-script/guides/html/reference/host#close

In Apps Script, the doGet() and doPost() functions are strictly for sending HTTP requests to GAS-based web apps. Spreadsheet-bound scripts can be published as web apps - however, according to the docs
To create a web app with the HTML service, your code must include a
doGet() function that tells the script how to serve the page. The
function must return an HtmlOutput object, as shown in this example.
In your case, the showModalDialog() method returns 'void'. Also,
Unlike a web app, a script that creates a user interface for a
document, spreadsheet, or form does not need a doGet() function
specifically, and you do not need to save a version of your script or
deploy it. Instead, the function that opens the user interface must
pass your HTML file as an HtmlOutput object to the showModalDialog())
or showSidebar() methods of the Ui object for the active document,
form, or spreadsheet.
Long story short, you don't need to deploy your script as a web app. Instead, you should put the UI building code directly into your main function and, finally, tie that function to the button.

Related

Bring up html file from sheets using apps script

I would like to have a google sheet that when you pick something on a google sheet it brings up an html file on a new google tab that has info from the google sheet. I am using the code below as a quick test to see if this is possible.
Right now I have a menu item that calls myFunction(), then I would like that to bring up the new html page. This doesn't come close to working, as it just enters and exits myFunction and no html page. So was wondering if anyone knows if this is possible. If so can you give me some hints on how to do it.
code.gs
function myFunction(){
HtmlService.createTemplate('page');
};
page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<title>Hello<\title>
</body>
</html>
Change myFunction() to doGet(), and you need to return the HtmlService from the doGet function. Also, the statement inside the doGet() function is different than what you currently have.
Your myFunction() function should look like this
function doGet() {
return HtmlService.createTemplateFromFile('page').evaluate();
}
doGet() is a special function that Google Apps Script web app scripts should handle any GET requests.
For more information, please refer to one of these guides:
https://developers.google.com/apps-script/guides/html/templates
https://developers.google.com/apps-script/guides/html
This is absolutely possible.
What you want to implement is a web app.
Simple Example
function doGet() {
textOutput = ContentService.createTextOutput("Hello World! Welcome to the web app.")
return textOutput
}
Put this in a script file and then deploy the script as a web app - instructions. The deploy process will give you a link with which you can visit the web app and see your text output.
What apps script does is that every time someone visits the URL, the doGet function is run.
HTML Example
function doGet(){
let HTMLString = "<style> h1,p {font-family: 'Helvitica', 'Arial'}</style>"
+ "<h1>Hello World!</h1>"
+ "<p>Welcome to the Web App";
HTMLOutput = HtmlService.createHtmlOutput(HTMLString);
return HTMLOutput
}
As you have already seen, HtmlService is what to use to serve up HTML.
Linking to Spreadsheet
The best way IMO to approach this is:
Create a spreadsheet.
Create the script from the spreadsheet menu - this ensures that the script and sheet are linked.
Decide how you want to present the information from the Spreadsheet and get it with something like:
let file = SpreadsheetApp.getActive(); // getActive only possible if script and sheet are linked
let sheet = file.getSheetByName("Sheet1");
let range = sheet.getDataRange();
let values = range.getValues();
Figure out how to translate that into HTML.
Write your doGet function.
Deploy
Profit!
Docs
Google Apps Script Web Apps
HTML Service
Spreadsheet Service

Google sheets: Class google.script.run not working

My problem is simple. All the possible solutions I searched for online did not address my question.
Google's developer website for Class google.script.run (https://developers.google.com/apps-script/guides/html/reference/run#withSuccessHandler) showcased the method myFunction(...) (any server-side function).
I have copied their exact code and html code and deduced that the function doSomething() does not execute. Nothing gets logged.
I intend to use this to execute an HTML file so that I could play a sound file. I could do this so far with a sidebar popping up from the side, as discussed in this thread: Google Script: Play Sound when a specific cell change the Value.
However, this code provided by Google does not work. Why?
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function doSomething() {
Logger.log('I was called!');
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
google.script.run.doSomething();
</script>
</head>
<body>
</body>
</html>
By using google.script.run you are calling a server-side Apps Script function.
https://developers.google.com/apps-script/guides/html/reference/run
Please double-check that you follow the following steps to do it correctly:
Please make sure that you put the html part of the code in a separate HTML file (which you create through File->New->HTML file) with the name corresponding to the one you are calling in HtmlService.createHtmlOutputFromFile() - in your case Index.html
Select “doGet” as the function to be run.
Deploy the script as a web app - this is the requirement for using Apps Script HTML service. Please find the instructions here: https://developers.google.com/apps-script/guides/web
Make sure that every time after you implement changes in your code, you deploy the script as a NEW project version. This is necessary to update the changes.
Open the current web app URL you obtain after updating your version, to open your html output.
In your case only an empty HTML file will be opened, to test functionality - insert some text in your HTML body, to test the correct functionality. The latter can be confirmed by viewing the Logs after running the code.

Redirecting from doPost() in Google Web App, HTML form with App script

Say I have an HTML file with various inputs. According to these inputs I would like to send an email and then display a thank you message or redirect to a thank you page.
I am loading the index.html file in the doGet() as below:
function doGet(){
var template = HtmlService.createTemplateFromFile('index');
template.action = ScriptApp.getService().getUrl();
return template.evaluate();
}
After adding the implementation which I require in doPost(e) function, deploying the code into a web app, filling in the form and submitting, everything seems to be working perfectly apart from the last bit. I want to show an output message or redirect to a thank you page, or something of the sort, but all I am seeing is a blank page, where there would have been the HTML form.
I have tried:
function doPost(e) {
//all logic
return ContentService.createTextOutput("Thank you");
}
And..
function doPost(e) {
//all logic
return ContentService.createTextOutput(JSON.stringify(e.parameter));
}
And..
function doPost(e) {
//all logic
var htmlBody = "<h1>Thank you.</h1>";
return HtmlService.createHtmlOutput(htmlBody);
}
And..
function doPost(e) {
//all logic
var template = HtmlService.createTemplateFromFile('Thanks');
return template.evaluate();
}
In all the listed cases the HTML form simply seems to disappear and I can only see a blank screen.
Any ideas?
In GAS, sending a POST request via web app is not as simple as it seems. Go ahead and try a little experiment: add the following JS code to the HTML page served by the doGet() function just before the closing <body> tag:
...
<script>
console.log(window.location);
</script>
</body>
When inspecting the output in the console, you'll likely get something like:
The issue is that the web app URL ending with '/exec' doesn't actually link to self. Ratner, the googleusercontent.com link is the URL you'll be redirected to when opening links and forms. However, in order to get the form submit to work, the POST request should be sent to the "published" URL of your web app (the one ending with '/exec').
You must therefore override the defaults by implementing your own routing. There are many ways to achieve this result - here's the easiest one:
<form method="post" action="<?!= ScriptApp.getService().getUrl() ?>">
<input type="submit" value="submit">
</form>
If the <?!= ?> notation looks unfamiliar to you, please check the section on scriptlets and HTML templates in GAS https://developers.google.com/apps-script/guides/html/templates
Basically, what happens is that the web app URL is force-printed to the template before raw HTML is sent to your browser for rendering. This makes sure you'll hit the correct URL when 'submit' event occurs.

Way to execute Google Standalone Script directly from Drive

Well, I've been reading the documentation for a long time but, as per usual, it's really unclear...
I'm developing a Google Script with the Script Editor that is already working as expected (it have nothing to see with any G suite program but with Drive).
By now, the only way to execute it is opening the script editor and press on the "play" button or the "execute" bar and press on the function.
Obviously this is not a solution since anyone from the bussines could modify/see the code.
So the question is this: how can I make from this script something like an "exe" that I just have to double ckick (its obviously located at Drive) and it executes the script?
I saw this but seems it says no way to do it except from opening the code and execute from the google app script editor...
A Web App is triggered by either an HTTPS GET or POST request. You don't need to use Google Drive.
Ways to trigger a Web App:
Chrome Extension - You can create a Chrome Extension to make an HTTPS GET or POST request. The user would need to install the Chrome extension, and the Chrome extension could put a button in the browser.
Link - Some HTML with a link in it. An email with a link in it. Click the link.
Address Bar - Browser's address bar - Every browser's address bar issues an HTTPS GET request, so you can run a Web App directly from the address bar. Put the published Web App url into the browser address bar and click whatever the browser uses to load the page. (Only for a GET request)
Bookmark - The user could bookmark the link to your Web App. So, they would need to click the link in their bookmark.
Any program that can make an HTTPS GET or POST request. For example, make a POST request from Python or C++.
For a GET request, you need a doGet() function in your script, and to react to a POST request you need a doPost() function.
From what I understand of your requirements the only way would be to deploy your script as a web app with a minimal user interface, just a short message to confirm proper execution for example.
You will never have a "local" executable file since everything in Apps Script is done on google's servers, not in our computers. Instead you will have an url... (with the advantage that it is completely OS independent ! )
The script will remain private unless you share it and you'll be able to choose who can use that url.
Try this:
Build the menu first.
Click "Run My Code".
Click the "Run My Code" button.
runmycode.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function runMyCode()
{
google.script.run
.withSuccessHandler(updateMyDiv)
.myFunctionName();
}
function updateMyDiv(hl)
{
document.getElementById('MyDiv').innerHTML+='<br />'+ hl;
}
</script>
</head>
<body>
<input type="button" value="Run My Code" onclick="runMyCode()" />
<div id="MyDiv"></div>
</body>
</html>
runMyCode.gs:
function myFunctionName() {
return "Your function has run";
}
function startYourSideBar()
{
var ui=HtmlService.createHtmlOutputFromFile('runmycode');
SpreadsheetApp.getUi().showSidebar(ui);
}
function buildMenu()
{
SpreadsheetApp.getUi().createMenu('Run My Code')
.addItem('Run My Code', 'startYourSideBar')
.addToUi()
}

google closure library usage from google app scripts using HtmlService

Is it possible to access google closure library functions from google app scripts via HtmlService? The html files in the google scripts seems to be filtering out anything related to closure library.
project: I am exploring DOM manipulation utilities from Google Closure library from within the google app scripts using HtmlService. I intend to run this as a stand alone web app.
The closure functions work when directly loaded into the browser from its local client environment - but they dont work when injected from GAS app via the HtmlService utility.
Here is the code I am using in the GAS.
html file
<html>
<head>
<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script>
goog.require('goog.dom');
function c_sayHi() {
var newHeader = goog.dom.createDom('h1', {'style': 'background-color:#EEE'},'Hello world!');
goog.dom.appendChild(document.body, newHeader);
}
</script>
</head>
<script>
function c_updateButton(date, button) {
button.value = "clicked at " + date;
}
</script>
<body onload="c_sayHi()">
<input type='button' value='Never Clicked'
onclick='google.script.run.withSuccessHandler(c_updateButton).withUserObject(this).s_getCurrentDate()'>
<input type='button' value='Never Clicked'
onclick='google.script.run.withSuccessHandler(c_updateButton).withUserObject(this).s_getCurrentDate()'>
</body>
</html>
Google Script file
function s_getCurrentDate() {
return new Date().toString();
}
function doGet(e) {
return HtmlService.createTemplateFromFile('hello').evaluate();
}
I have prefixed c_ to client side functions and s_ for server side fns. When running this as a web app,
Function c_sayHi has no effect - I am not sure if it is even invoked.
Functions s_getCurrentDate and c_updateButton work fine as described in google's documentation https://developers.google.com/apps-script/html_service.
Is there a way to get closure library working from the web apps as attempted above?
Couple of things here -
All .gs files is JavaScript that runs on the server side. So the DOM is not really relevant there.
You can run client side JavaScript by returning code in HtmlService. This is what I believe you want to do. However, jQuery is the best supported library on this approach. Closure might end up working but the team does not specifically test against that library.
The problem is that Closure's dependency structure is executing before the window load event, otherwise it will not work. So any require and provide statements are taken care of way before window load. When you inject them through the HTML Service, you are forcing their execution at a different stage then required, which causes everything to fail.
If you would be using a COMPILED Closure Library source, you will not have any problems with running Closure. Learn how to use the Compiler and Builder to make Closure Work properly. Also, you can use lazy loading to simulate your HTML Service.
With that, you can make javascript load dynamically onclick, onload or whatever the hell you want. This is called lazy-loading and it is used as a standard practice for all large web applications. Monitor the Network tab of Firebug when browsing through Gmail or Facebook.
Arun Nagarajan is right, jQuery is the easier solution but if you are doing something proper that requires breadth, scale and speed, jQuery is a toy for kids.