RequireJS with Google Apps Script - google-apps-script

I am wondering if it is possible to use RequireJS with Google Apps Script. I am loading RequireJS 2.1.5 from a GAS library. This sample app should log the word Monkey but nothing happens when I test it. No errors or warnings are being generated either.
index.html:
<!doctype html>
<head>
<title></title>
</head>
<body>
<script data-main="main" src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.5/require.js"></script>
</body>
</html>
main.gs:
var define = Require.loadDefine();
var require = Require.loadRequire();
define( function() {
Logger.log("Monkey");
});
code.gs:
function doGet(e) {
Logger.log("doGet");
return HtmlService.createHtmlOutputFromFile('index').setSandboxMode(HtmlService.SandboxMode.NATIVE);
var t;
t = HtmlService.createTemplateFromFile('index');
return t.evaluate();
}

I found the answer while reading JavaScript Module Pattern: In-Depth
The code inside the module didn't have access to Logger. Passing this as a parameter makes it work.
define( function(g) {
g.Logger.log("Monkey");
}(this));

Related

Apps Script - Use a Code.gs variable in HTML script

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

How do I get the title I set through HtmlOutput's setTitle from the html side?

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

Bookmarklet hosted in Apps Script unable to run when shared

After deploying my web app in Apps Script with bookmarklet, it works fine on my end however not when I tried to share my web app, it throws an 'unsafe javascript' error like its being clicked from the web app and not as a bookmarklet.
The web app is run as user accessing and can be accessed by anyone in our organization.
What bookmarklet does is run a prompt for input and find it on the current page.
The code goes like this:
<a id="bkmark">Link</a>
<script>
document.body.onload=()=>{
google.script.run.withSuccessHandler(rep).getLink();
function rep(e){
document.querySelector('#bkmark').href = e; // return a javascript: IIFE wrapped in ``
}
}
</script>
And my gs returns Html output from a file.
Any help appreciated, Thanks!
Instead of adding the JavaScript as a booklet, you can directly import using ContentService and templates:
Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="<?= ScriptApp.getService().getUrl() + '?getAction=true' ?>" type="text/javascript" defer async></script>
</head>
<body>
<button id="trigger" onclick="doAction()" disabled>Click here to do something</button>
</body>
</html>
Code.js
function doGet(e) {
const { getAction } = e.parameter
if (getAction !== undefined) {
return genAction()
}
return HtmlService.createTemplateFromFile('Page').evaluate().setTitle("Booklet webapp")
}
function genAction() {
return ContentService
.createTextOutput(`
function doAction() {
alert("Hello world!")
}
(function loaded() {
document.getElementById("trigger").disabled = false
})();
`)
.setMimeType(ContentService.MimeType.JAVASCRIPT)
}
Notice that the script tag has defer and async. This makes it so the script doesn't start loading after the DOM has been fully loaded and it starts doing so asynchronously (without blocking the main thread).
On the Apps Script side, we use a template so we can dynamically get the script execution URL (with ScriptApp.getService().getUrl()) and we add a query parameter (getAction) so we can detect it on the doGet(e) function. There we check for that parameter and we have it we return a JavaScript content instead.
Also made the button disabled by default and I added a small code so the button get enabled when the script has finished loading (it's asynchronous so it can take some time to fully load).
This technique can also be used to dynamically add an script tag by fetching the result, creating the script element, and adding the contents manually.
References
Class ContentService (Google Apps Script reference)
Class HtmlTemplate (Google Apps Script reference)
HTML Service: Templated HTML (Google Apps Script guides)
HtmlService.createTemplateFromFile(filename) (Google Apps Script reference)
Service.getUrl() (Google Apps Script reference)

Google Script pulling dynamic HTML iFrame from Google Sheet Cell

I am working on a incorporating a Google Form as an iFrame in a Modal Dialog box on my Google Spreadsheet (it is already accessible through a customer menu) with iFrame code that dynamically changes based on what's going on in the spreadsheet.
My Google Script to reference the HTML file is:
//Help Guide
function openHelp() {
var html = HtmlService.createHtmlOutputFromFile('Help Guide').setHeight(560).setWidth(814.81).setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(html, ' ');
}
When I paste the generated iFrame code into the HTML file 'Help Guide', it loads as desired. However, I would like to instead reference my iFrame code that is housed on Sheet1!C1 when I run the openHelp() function from the menu. This would enable me to dynamically change the iFrame based on the spreadsheet. I have explored using Google's HTML Template service, but haven't been able to figure it out.
I am sure the script is quite easy, but I am unfamiliar with what code needs to be written in the HTML file to pull that generated iFrame code from Sheet1!C1.
Thanks!
Get some HTML code from a Cell in a Spreadsheet
You need to update the variables iframeSheet and iframeCell.
Create the iframe statement.
Deploy as a web app. The doGet is already there. You can also run the showIframeDialog() function to see it work.
The Code:
function getIframeCode()
{
var iframeSheet='Sheet1';
var iframeCell='A1';
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(iframeSheet);
var hl=sh.getRange(iframeCell).getValue();
return hl;
}
function showIframeDialog()
{
var ui=HtmlService.createHtmlOutputFromFile('iframecode');
SpreadsheetApp.getUi().showModelessDialog(ui, 'Dynamic Iframe')
}
function doGet()
{
var ui=HtmlService.createHtmlOutputFromFile('iframecode');
return ui.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
The iframecode.html file:
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
google.script.run
.withSuccessHandler(updateIframeCode)
.getIframeCode();
});
function updateIframeCode(hl)
{
document.getElementById("iframe").innerHTML=hl;
}
console.log("My code");
</script>
</head>
<body>
<div id="iframe"></div>
</body>
</html>

How can I reference a Google Apps Script .html file from another library?

I'm writing a set of HTML pages that will be served on the Google Sheets sidebars and will connect to our internal database. Each page is set up on its own .html file within a single Google Apps Script project.
I'd like to pull these files out to a single project and reference this as a library like I can do with my other .gs script files. Specifically, how can I write the "MyLib.page" line below?
Library project:
Code.gs
function myFunc() { Logger.log("Hallo Werld!");}
page.html
<h1>
Hello world!
</h1>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
google.script.run.myFunc();
});
</script>
Spreadsheet Script Editor (Included Library Identifier: MyLib)
Code.gs
function callLibFunc() {
MyLib.myFunc();
}
function loadSidebar() {
var html = HtmlService.createTemplateFromFile("MyLib.page").evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showSidebar(html);
}
Make the Lib open the Sidebar:
Library project:
Code.gs
function loadSidebar() {
var html = HtmlService.createTemplateFromFile("MyLib.page").evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showSidebar(html);
}
Spreadsheet Script Editor
Code.gs
function callLibFunc() {
MyLib.loadSidebar();
}