Pass Google Script Array to Javascript Function (Google Apps) - google-apps-script

I understand why my code does not work, however I can't find a solution searching online. Likely a simple syntax correction. Here is the code:
Code.gs
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getData() {
return SpreadsheetApp
.openById('My Sheet ID')
.getActiveSheet()
.getDataRange()
.getValues();
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
h1 {
width: 100%;
overflow: hidden;
background-color: #ABC;
}
#wrapper {
margin: auto;
width: 60%;
border: 3px solid #000;
padding: 10px;
}
</style>
</head>
<body>
<div id="wrapper">
<h1>Attendace Discrepancy Tracker v1.0</h1>
<h3>Discrepancy date:</h3><input type="date" name="bday" min="2000-01-02"><br>
<? var data = getData(); ?> <------ Gets data from my sheet
<h3>Select a name:</h3><select id='empName' onchange="findEmpID();">
<? for (var i = 1; i < data.length; i++) { ?>
<option value="<?= i ?>"><?= data[i][2] + ", " + data[i][1] + " " + data[i][3]?></option>
<? } ?>
</select>
<div id="empProfile">
<p>Employee Number: <span id="empNumber"></span></p>
<p>Supervisor: <span id="empSup"></span></p>
</div>
</div>
<script>
function findEmpID () {
var e = document.getElementById("empName");
var strUser = e.options[e.selectedIndex].value;
document.getElementById("empNumber")
.innerHTML = <?= data[strUser][0] ?>; <----- This does not work
}
</script>
</body>
</html>
The whole program works, except when I try and reference back to the data array. I need a way to pass the <script> tags strUser value to the data array google script variable so I can get the supervisors ID number. Obviously when I switch to the <? tag space it cannot read variables from the <script> tag. I am sure it is possible, just have not figured out how.

You are trying to use a scriptlet after the HTML has loaded. It looks like you are trying to trigger the scriptlet to run from the onchange attribute of the drop down list. I don't think that scriptlets will run except when the page is first loaded.
I'd change things like this:
Use the this keyword to pass the employee name to the client side findEmpID() function:
<h3>Select a name:</h3><select id='empName' onchange="findEmpID(this.value);">
Note the use
of this.value. Enter a variable name in the function parenthesis to receive the employee name:
function findEmpID(theName) {
Pass the name to the server function:
<script>
function findEmpID(theName) {
google.script.run
.withSuccessHandler(enterName)
.getThisEmployeeID(theName);
};
function enterName(theReturnedName) {
//Put the name into the HTML Element
document.getElementById("empNumber").textContent = theReturnedName;
};
</script>
You'll need to get the data from the spreadsheet, and find the name with the server function:
GS code
function getThisEmployeeID(theName) {
//Get range of data
var arrayOfData = sheet.getRange(parameters).getValues();
//I don't know how the data is configured, but you'll need to match
//the name to the index number in the array, then get the ID
var employeeID = 'xyz';
return employeeID;
};

Related

Apps-script function with dynamic parameter

I am testing the app script platform and I have a doubt when using this code called from HTML file:
JSON.parse(<?= JSON.stringify(getDataFromSheet("tyreUse", "valueSearched")); ?>);
If I set the string value directly it works.
If I try to pass a variable that is declared in it does not recognize it. How can I pass a JS variable to the app script function like next example?
let value_searched = "cars";
JSON.parse(<?= JSON.stringify(getDataFromSheet("tyreUse", value_searched)); ?>);
Scriptlets like <?= ?> are used in html templates to load data from the server into html pages prior to rendering. If you want to pass data back to a server side function then you can use google.script.run and there are restrictions on the data types that you can pass.
google.script.run
Here is an example of getting data from spreadsheet dynamically. I typically build my page and then use an anonymous function of the form (function () {}()); to get the data from spreadsheet and populate the HTML elements with the values.
Create an HTML file HTML_Demo:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input id="A8SBwf" type="text">
<input id="gNO89b" type="button" value="Click Me" onclick="buttonOnClick()">
<script>
function buttonOnClick() {
try {
google.script.run.withSuccessHandler(
function(response) {
document.getElementById("A8SBwf").value = response;
}
).getCellA1();
}
catch(err) {
alert(err);
}
}
</script>
</body>
</html>
Then in Code.gs create the getCellA1:
function getCellA1() {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var range = sheet.getRange("A1");
return range.getValue();
}
catch(err) {
return err.message;
}
}

Google App Script: how to display google drive files with links to templated HTML

I'm new in both Google App Scripting and JavaScript. So, I'm trying to display the grabbed files from my Google Drive with links however when running displayData(); it's literally showing the link and the title of the file on the page without the actual link in it. Here's picture of the html
output.
Here's what I have so far:
Code.gs
function doGet() {
var output = HtmlService.createTemplateFromFile('Page').evaluate();
return output;
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function displayData() {
var dir = 'Somefoldername';
var foldername = DriveApp.getFoldersByName(dir).next();
var foldercont = foldername.getFiles();
var listicon = '<img src="https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.document">';
var file, title, links, list = [];
while (contents.hasNext()) {
file = foldercont.next();
title = file.getName();
links = file.getUrl();
date = file.getDateCreated();
list.push('<tr><td>' + listicon + '<a href ="' + links + '">' + title +'</td></tr>');
}
return list;
Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('Stylesheet'); ?>
</head>
<body>
<div class="doclist">
<?= displayData(); ?>
</div>
</body>
</html>
How would I display the Google Drive file hyperlinks in the html template?
Thank you.
Here is a Sample Code:
Note:
I temporarily removed <?!= include('Stylesheet'); ?> in the html file since it is not defined.
Code.gs
function doGet() {
var output = HtmlService.createTemplateFromFile('Page').evaluate();
return output;
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function displayData() {
var dir = 'Somefoldername';
var foldername = DriveApp.getFoldersByName(dir).next();
var foldercont = foldername.getFiles();
var listicon = '<img src="https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.document">';
var file, title, links, list = [];
while (foldercont.hasNext()) {
file = foldercont.next();
title = file.getName();
links = file.getUrl();
date = file.getDateCreated();
list.push('<tr><td>' + listicon + '<a href ="' + links + '">' + title +'</td></tr>');
}
return list.join(' ');
}
Modifications done:
Replace while (contents.hasNext()) with while (foldercont.hasNext())
Combine your array list into a single string using array.join(' ') with spaces as its separator
Page.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<script>
google.script.run.withSuccessHandler(function(tblStr){
document.getElementById('FileList').innerHTML = tblStr;
}).displayData();
</script>
<table id="FileList">
</table>
</body>
</html>
Modifications done:
I added a table in the html body with the id "FileList"
I called the server-side function displayData() using google.script.run.withSuccessHandler(function).displayData(). The return value of displayData() will be passed to the callback function's first parameter.
I updated the table's content based on the displayData()'s return value using this procedure document.getElementById('FileList').innerHTML = tblStr;
Output:
Some observations:
The function is returning an array list = [], and you are pushing data into that array.
Your HTML in the screenshot has stray commas in it between each item: ...</td></tr>,<tr><td>....
You shouldn't place a <div> inside a table.
Both of these suggest that you should be appending your data to a string variable, instead of pushing data into an array.
Then return that string from your function, instead of the array.
The string variable will contain the entire contents of your HTML rows and columns.
For the <div>, remove it and place the class in the body:
<body class="doclist">
Depending on how your CSS is set up, that may need modifying to accommodate this change.
Final suggestion: take the resulting HTML which is generated and run it through a validator - for example: https://validator.w3.org/#validate_by_input
That may find some additional issues which need correcting.

Holding value in textarea for Google Script

I'm trying to write code that will help some of my users take a JSON response and convert it to a table in Google Sheets. I have the code for the JSON to Table script, courtesy of Amit Agarwal at www.ctrlq.org.
The way that I'm trying to do is that Google Sheets will generate a pop up for my user to copy and paste the JSON straight into it and then it will pass the object to the JSON to Table code. I'm having a problem figuring out how to actually do that.
Code.gs
function showPrompt()
{
var ui = SpreadsheetApp.getUi(); // Same variations.
var html = HtmlService.createHtmlOutputFromFile('Upload');
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Dialog title');
}
function setJSON(json)
{
var json = json; //json object?
return json;
}
// Written by Amit Agarwal www.ctrlq.org
function writeJSONtoSheet(json) {
var sheet = SpreadsheetApp.getActiveSheet()[1];
var keys = Object.keys(json).sort();
var last = sheet.getLastColumn();
var header = sheet.getRange(1, 1, 1, last).getValues()[0];
var newCols = [];
for (var k = 0; k < keys.length; k++) {
if (header.indexOf(keys[k]) === -1) {
newCols.push(keys[k]);
}
}
if (newCols.length > 0) {
sheet.insertColumnsAfter(last, newCols.length);
sheet.getRange(1, last + 1, 1, newCols.length).setValues([newCols]);
header = header.concat(newCols);
}
var row = [];
for (var h = 0; h < header.length; h++) {
row.push(header[h] in json ? json[header[h]] : "");
}
sheet.appendRow(row);
}
Dialog box HTML:
<!DOCTYPE html>
<html>
<head>
<script>
function setJSON() {
// how do i add value to set to set JSON
var json = ""
google.script.run.setJSON(json);
}
</script>
<base target="_top">
</head>
<body>
text text
<textarea rows="20" cols="20" placeholder="Paste your Data here. Do not format. Do not worry if it looks weird." name="json">DATA</textarea>
<input type="button" class="button" value="Submit JSON" onclick="setJSON()">
</body>
</html>
Thank you in advance!
Here's a simple example of triggering a dialog via an in-sheet menu and passing the input back to a server side function.
Code.gs
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Input Form Test')
.addItem('Input Form', 'showInputForm')
.addToUi();
}
function showInputForm() {
var html = HtmlService.createHtmlOutputFromFile('inputform');
SpreadsheetApp.getUi().showModalDialog(html, 'Input Form Test');
}
function logInput(input) {
Logger.log(input)
}
inputform.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script>
function setJSON(form) {
var jsonData = document.getElementById('json-data');
google.script.run.logInput(jsonData.value);
google.script.host.close();
}
</script>
<body>
<textarea id="json-data" rows="20" cols="20" placeholder="Paste your data here. Do not format. Do not worry if it looks weird." name="json">DATA</textarea>
<input type="button" class="button" value="Submit JSON" onclick="setJSON()">
</body>
</html>
Logging Output
After clicking the "Submit JSON" button with "test input" entered into the form, this is the output in the script logs:
[18-06-15 13:47:15:741 PDT] test input
Notes
I added an id attribute to the textarea element
the id is used to fetch the element programmatically
you may want to perform some client-side validations in setJSON(). For example:
verify that the value is not still the default value (i.e. DATA)
validate that the JSON data can be parsed succesfully (i.e. JSON.parse())
an example implementation with the above validations:
function setJSON(form) {
var jsonData = document.getElementById('json-data');
if (jsonData.value == 'DATA') {
alert("Please enter JSON data into the form.");
return;
}
try {
JSON.parse(jsonData.value)
}
catch(e) {
console.log(e); // Outputs to the browser console.
alert("Unable to parse JSON data.");
return;
}
google.script.run.logInput(jsonData.value);
google.script.host.close();
}
to fit your use case, in inputform.html you'd replace the logInput() call with either:
a function that calls JSON.parse() and then calls writeJSONtoSheet() with the result
writeJSONtoSheet() if you update it to expect text and add a JSON.parse() call at the beginning of the function

Returning Colored HTML

I'm trying to have my Google App Script return HTML that is colored based on a call to Google Calendar. Basically a green "Available" if there are no events on calendar and a red "Unavailable" if there are events.
function getavail(instr) {
var today = new Date();
var scriptProperties=PropertiesService.getScriptProperties();
var instrcal= scriptProperties.getProperty(instr);
var event=CalendarApp.getCalendarById(instrcal).getEventsForDay(today);
if (event<1) {
return HtmlService.createHtmlOutput('<font color="#00cc00">Available</font>')
}
else {
return HtmlService.createHtmlOutput.('<font color="#ff0000">Unavailable</font>') ;
}
}
In the page HTML, I am calling function getavail through a scriplet
<?= getavail('instr') ?>
And for some reason, it renders like this instead...
EXPLORER (2386): HtmlOutput
Any help would be much appreciated. For reference I was using the example from https://developers.google.com/apps-script/reference/html/html-output
You just need to return your html as a string in your getavail(), then using a force-printing scriptlet with the syntax <?!= ... ?> will do the trick.
Also note that the getEventsForDay() returns an Array of events, so to get the number of events you need to check the length
Here's an example:
function getavail() {
var today = new Date();
var calendarId = "{YOUR_CALENDAR_ID}";
var events = CalendarApp.getCalendarById(calendarId).getEventsForDay(today);
if (events.length < 1) {
return '<font color="#00cc00">Available</font>';
}else {
return '<font color="#ff0000">Unavailable</font>';
}
}
And in your HTML:
<?!= getavail() ?>

Pass array from server side function using google script and html

I have an html page that will be served to a google sheet app to be used as a UI. I would like to access an array from a server side function within the html file. I am having trouble accessing a returned array. Here is what I have:
in html file:
<div id="id1">
Starting 1
</div>
<div id= "id2">
Starting 2
</div>
<script type="text/javascript">
document.getElementById("id1").innerHTML = "A change";
</script>
<script type="text/javascript">
function onSuccess(numUnread) {
alert('You have ' + numUnread[0]
+ ' unread messages in your Gmail inbox.');
document.getElementById("id2").innerHTML = numUnread[0];
}
google.script.run.withSuccessHandler(onSuccess)
.getPermits();
</script>
In code.gs:
function getPermits()
{
var permits = [];
for(var i = 0; i < 10; i++)
{
permits.push('Element ' + i);
}
return permits;
}
Right now I am just trying to figure out why the div with id = "id2"
does not get changed to the first element from the passed array. Instead, it is not changed. Also, there is no alert. If I change the return of the gePermits() function to a string, both the div and the alert work as I would expect.
Thanks in advance!
Some types are not passed trough HTMLService, but you can always STRINGFY and PARSE it, try:
return JSON.stringify(permits);
and in the html:
function onSuccess(numUnread) {
numUnread = JSON.parse(numUnread);