So, I've been looking through StackOverlow for a while now, and the closest answer I found is this.
I have built a template for sending emails and I'd like to reuse it for other purposes, such as the description of an event. I'm struggling to get a printout of the HTML template in a readable format, exactly as you get it in the email.
Here's the HTML template:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<p>Hi <?!= OOOCLIENTFIRSTNAME?>,</p>
</body>
</html>
Here's the template code:
var emailtemplate = HtmlService.createTemplateFromFile('XXXXX');
emailtemplate.OOOCLIENTFIRSTNAME = clientfirstname;
var emailbody = emailtemplate.evaluate();
So, when I place it into the sendEmail method parameter's htmlbody property, it works perfectly and email is sent as planned. Here's the GmailApp code:
GmailApp.sendEmail(clientemail, 'TEST TITLE', '',
{htmlBody: emailBody.getContent()};
I want to get the result as a simple string, such as "Hi firstname".
I have tried to use .getContent but this results in getting the HTML source code, and not its output. Any ideas?
I suggest you a different approach:
Just build your greeting in the Apps Script part before passing it to HTML.
Code:gs
function myFunction() {
var emailtemplate = HtmlService.createTemplateFromFile('index');
var greeting = "Hi " + clientfirstname + ",";
emailtemplate.OOOCLIENTFIRSTNAME = greeting;
var emailBody = emailtemplate.evaluate();
GmailApp.sendEmail(clienemail, 'TEST TITLE', '',{htmlBody: emailBody.getContent()});
var reusedGreeting = greeting + " how are you?";
Logger.log(reusedGreeting);
}
index.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<p> Hi <?!= OOOCLIENTFIRSTNAME?>,</p>
</body>
</html>
Why?
When you evaluate a template within a normal function, (opposed to building a WebApp with a doGet() function that would allow you to pass a variable to serverside with google.script.run) there is no easy way to retrieve the inner HTML.
You'd need to either
extract the string with Regex
or parse the html as explained e.g. here
Both solutions would be an overkill for what you are trying to do.
Related
I'm trying to open a print window for a shipping label (in pdf form) from a google drive file link.
This is the .gs code I've written so far:
the drive link is being declared based on an order its associated with, that portion isn't applicable in this instance
function printPdf() {
const sheet = SpreadsheetApp.getActive().getSheets()[4];
const ui = SpreadsheetApp.getUi();
// order ID associated with the label
const orderQuery = sheet.getRange('S:S').createTextFinder(
ui.prompt('Enter an order ID', '', ui.ButtonSet.OK).getResponseText()
).matchEntireCell(true).findNext();
let url;
// drive link is in column 'O'
if (orderQuery) url = sheet.getRange(orderQuery.getRow(), 15).getValue();
const template = HtmlService.createTemplateFromFile('check-out-modeless');
template.url = url;
const html = template.evaluate();
ui.showModelessDialog(html, 'Print Label');
}
and the HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<iframe src="<?= url ?>" style="width:100%; height:100%;"></iframe>
<script>
window.print();
google.script.host.close();
</script>
</body>
</html>
So far I
made sure the drive like is id-based rather than preview-based
verified that the drive link is accessible from my account
I used an incognito window as well to avoid conflicts from being logged in to more than one account
recorded network headers to see if there was any indication of why the server is declining access
Beyond those I can't think of any other way to solve this aside from maybe utilizing blobs & DriveApp
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
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've built an HTML file inside the Google Script code editor.
Inside the HTML I've included placeholders, such as {{NAME}}.
My goal is to replace these placeholders with data from a Google Sheet.
Well...it's not working :)
The email is received with just HtmlOutput in the body.
Code below. What am I missing?
var emailBody = HtmlService.createHtmlOutputFromFile('client_intro_email');
emailBody = emailBody.replace({{NAME}}, clientname);
GmailApp.sendEmail(clientemail, 'subject', '',
{htmlBody: emailBody.getContent(),
}
Hi {{NAME}},
Here are the variables I'd like to push into the HTML. Content of the cells is text.
var clientname = spreadsheet.getRange('C2').getValue();
var clientemail = spreadsheet.getRange('P2').getValue();
Answer:
You can use templated HTML to dynamically insert HTML content from your script.
More Information:
From the documentation on scriptlets:
Apps Script templates can contain three special tags, called scriptlets. Inside a scriptlet, you can write any code that would work in a normal Apps Script file: scriptlets can call functions defined in other code files, reference global variables, or use any of the Apps Script APIs. You can even define functions and variables within scriptlets, with the caveat that they can't be called by functions defined in code files or other templates.
So, you can create an HTML Template file (from the File > New > HTML file menu item in the Apps Script UI) and set it up ready to receive data from whatever your Apps script function processes.
Setting up your HTML:
As a simple example, you can create an HTML file called emailTemplate.html which contains the following data:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
Hi <?!= name ?>, your email address is <?!= email ?>.
</body>
</html>
Then in your Code.gs file, you can load the data you want directly into your HTML template before evaluating it:
var htmlTemplate = HtmlService.createTemplateFromFile('emailTemplate.html');
var clientname = spreadsheet.getRange('C2').getValue();
var clientemail = spreadsheet.getRange('P2').getValue();
htmlTemplate.email = clientemail;
htmlTemplate.name = clientname;
var emailBody = htmlTemplate.evaluate();
GmailApp.sendEmail(clientemail, 'subject', '',
{
htmlBody: emailBody.getContent(),
});
This mitigates any need to do replacing with emailBody.replace({{NAME}}, clientname); as the template is directly loaded with the variables that have been set in Apps Script.
For a person called John Doe with an email address of john.doe#example.com, the above example will send an email which will appear as:
Hi John Doe, your email address is john.doe#example.com.
References:
HTML Service: Templated HTML | Apps Script | Google Developers
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));