Google Apps Script Return HTML Syntax Error - google-apps-script

Hey guys I'm very new to coding and I have encountered a Syntax error that I can't seem to figure out. I've looked online for the error and what's worse, I've managed to get the script to work earlier today before receiving the error. I am trying to take rows from a Google Sheet and pin them onto a map.
The error I receive is on line 9 where I try to return the array. Any input or suggestions would be much appreciated it! Thank you guys for trying to help. Specifically:
return html.evaluate().setTitle('Company Directory Map');
Here is the code that I have:
function doGet(e) {
var html = HtmlService.createTemplateFromFile('DirectoryHTML');
var ss = SpreadsheetApp.getActiveSpreadsheet();
html.ss = parseDirectory(ss.getSheetByName('Directory Address & Name').getRange('A2:R').getValues());
return html.evaluate().setTitle('Company Directory Map');
}
function parseDirectory(values) {
var locations = [];
for (var i = 1; i < values.length; i++) {
locations.push({
LocationID: values[i][1],
LocationName: values[i][4],
Address: values[i][6],
AddressDetail: values[i][7],
City: values[i][8],
State: values[i][9],
Zipcode: values[i][10],
ZipcodeExtension: values[i][11],
mapURL: getMapUrl(values[i][6,8,9,10,11]),
Phone: values[i][12],
Fax: values[i][13],
EnteralPhone: values[i][14],
EnteralFax: values[i][15],
Type: values[i][16],
Notes: values[i][17]
});
}
return locations;
}
function getMapUrl(city) {
return Maps.newStaticMap().setSize(1200, 600).setCenter(41.37132419162449,-112.13662837438801).getMapUrl();
}
Edit
Below is my HTML code for reference:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? = for(var i = 0, i < locations.length; i++) {?>
<div>
<img src = "<? locations[i].mapUrl ?>" />
<h2><?= locations[i].LocationID ?></h2>
<!-- <h4><?= locations[i].Address ?></h4> -->
</div>
<? } ?>
</body>
</html>

The reason for syntax error is you have syntax error in your HTML code, specifically this line
<? = for(var i = 0, i < locations.length; i++) {?>
1) You are using printing scriptlet, but there is no variable/output to print. Use standard scriptlet instead <? ?>
2) The proper syntax for the loop is for(var i = 0; i < locations.length; i++). Note the use of ; instead on , in the loop.
Finally, you set the value of ss object in you htmltemplate
html.ss = parseDirectory(ss.getSheetByName('Directory Address & Name').getRange('A2:R').getValues());
However, in your HTML code, you use the object locations to run through the loop. Hence modify your HTML code like so
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? var locations = ss ?>
<? for(var i = 0; i < locations.length; i++) {?>
<div>
<img src = "<?= locations[i].mapUrl ?>" />
<h2><?= locations[i].LocationID ?></h2>
<!-- <h4><?= locations[i].Address ?></h4> -->
</div>
<? } ?>
</body>
</html>

Related

Google Sheets table is not showing new lines "\n" correctly in App Script WebApp

I have a simple WebApp for getting a table data in a Spreadsheet.
What's the problem is, I have not getting Line Breaks in WebApp
Actual Data in Spreadsheet
Akash
Bangalore
but getting like this in WebApp
Akash Bangalore
Pls help me to do this ..
Spreadsheet URL
WebApp URL
Below is the Code.gs file
function doGet() {
template = HtmlService.createTemplateFromFile('Index.html');
return template.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
var spreadsheetId = '116XVmxdI5uQ4A2QsjLrI25pUQBCDL22KCkGXHZZfKVQ';
var sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Sheet1") ;
var dataValues = sheet.getDataRange().getDisplayValues() ;
Below is the Index.html file
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1>Display Google Sheet Web App</h1>
<table border="1" style="border-collapse: collapse; margin:auto" cellpadding="5px" >
<?var tableData = dataValues ?>
<?for(var i = 0; i < tableData.length; i++) { ?>
<?if(i == 0) { ?>
<tr>
<?for(var j = 0; j < tableData[i].length; j++) { ?>
<th><?= tableData[i][j] ?></th>
<? } ?>
</tr>
<? } else { ?>
<tr>
<?for(var j = 0; j < tableData[i].length; j++) { ?>
<td><?= tableData[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
<? } ?>
</table>
</body>
</html>
New lines \n in html are ignored. To get breaks in html, you should use <br/>. Use Array.map to loop:
var dataValues = sheet.getDataRange()
.getDisplayValues()
.map(row => row.map(value =>
value.replace(/\n/g, `<br/>`)
)
);
When passing a value to be displayed, i.e. as <?= tableData[i][j] ?> instead of printing scriptlets <?= ?> use force-printing scriptlets <?!= ?>
The above because when using the first, Google use contextual escaping, while with the other Google will show the data "as is".

Update scriptlets in HTML without reloading page

I am referring to this article about templated HTML and scriptlets with Google Apps Script:
https://developers.google.com/apps-script/guides/html/templates
It is written how to call an Apps Script function and load the data when loading the html page:
Code.gs:
function doGet() {
return HtmlService
.createTemplateFromFile('Index')
.evaluate();
}
function getData() {
return SpreadsheetApp
.openById('1234567890abcdefghijklmnopqrstuvwxyz')
.getActiveSheet()
.getDataRange()
.getValues();
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<? var data = getData(); ?>
<table>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</body>
</html>
Question:
Is it possible to update the scriptlets without refreshing the whole page?
For example to implement a button and call the getData() function to load new data from the spreadsheet to the html?
Update:
I have adjusted the code with a simple button and a code to call the getData() function again and update the table. But of course doing it this way the for loop gets lost. Better would be to reload the whole code.
Is there a way to re-evaluate the whole page?
Any ideas?
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button type="button" onclick="Refresh()">Refresh</button>
<? var data = getData(); ?>
<table id="datatable">
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</body>
<script>
function Refresh(){
google.script.run.withSuccessHandler(update).withUserObject(this).getData();
}
function update(returnValue){
document.getElementById("datatable").innerHTML= returnValue
}
</script>
</html>
If you will call getData() from the client-side, then you will need to build the table on the client-side. The easier way might be to create a string and then add it by using HtmlElement.outerHTML to replace the whole element or HtmlElement.innerHTML to replace the inner elements and content.
Client-side JavaScript code (put it between <script> tags, do not require to change your server-side code)
function refreshTable(){
google.script.run
.withSuccessHandler(updateTable)
.withFailureHandler((error) => console.error(error.message))
.getData()
}
function updateTable(data){
let html = '';
for (var i = 0; i < data.length; i++) {
html += `<tr>`;
for (var j = 0; j < data[i].length; j++) {
html += `<td>${data[i][j]}</td>`;
}
html += `</tr>`;
}
document.getElementById("datatable").innerHTML = html;
}
By the other hand, if you want to use Google Apps Script templated HTML to build the HTML table, then you need to call a function that evaluates the template to generate a HttpOutput object and then use HttpOutput.getContent() to return a string to the client side code, then you might pass this string to the corresponding element by using HtmlElement.innerHTML.
Server Side, in a .gs file
function includeFromTemplate(filename){
return HtmlService.createTemplateFromFile(filename).evaluate().getContent();
}
Server side, table.html file
<? var data = getData(); ?>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<? for (var j = 0; j < data[i].length; j++) { ?>
<td><?= data[i][j] ?></td>
<? } ?>
</tr>
<? } ?>
Client Side. Call this function from onclick attribute or use the HtmlElement.addEventlistener method.
function refreshTable(){
google.script.run
.withSuccessHandler(html => {
document.querySelector('table').innerHTML = html;
})
.withFailureHandler((error) => console.error(error.message))
.includeFromTemplate('table')
}

Get data from database and append data to aframe

I want to get data from database and append that data to aframe. I did and data is getting from the database but not appending to the aframe scene. Here is my working flow.
This index file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js">
</script>
</head>
<body>
<a-scene id="scene">
<a-camera id="camera" position="0 0 2" >
</a-camera>
<a-sky color="#000"></a-sky>
</a-scene>
<script>
var ajax = new XMLHttpRequest();
var method = "GET";
var url = "data.php";
var asychronous = true;
ajax.open(method,url,asychronous);
ajax.send();
ajax.onreadystatechange = function(){
if(this.readyState==4 && this.status==200){
var data = JSON.parse(this.responseText);
console.log(data);
var html = "";
var username = "";
for(var i=0;i<data.length;i++){
username = data[i].username;
html += "<a-scene>";
html += +username;
html += "</a-scene>";
}
var totalText1 = document.createElement('a-text');
totalText1.setAttribute('position',{x:0, y:0, z:0});
totalText1.setAttribute('color',"#fff");
totalText1.setAttribute('value',username);
totalText1.setAttribute('scale',{x:1.6, y:1.6, z:1.6});
document.getElementById("scene").appendChild(totalText1);
}
}
</script>
</body>
</html>
Here is data.php file
<?php
$conn = mysqli_connect("localhost","root","","test");
$query = "SELECT * FROM usertest WHERE language='english'";
$result = mysqli_query($conn,$query);
$data = array();
while($row = mysqli_fetch_assoc($result)){
$data = $row;
}
echo json_encode($data);
?>
Data retrieving is okay.But is there any way to append those data to aframe scene?
Double check the entity is actually getting appended to the scene. It looks right. Check the Inspector (ctrl/alt/i) or DOM Inspector or query selector from console. The 0/0/0 position might just make it hard to see.

Very simple Google-apps script results in syntax error

I have a collection of Google App. scripts that I run as standalone apps., usually embedded into Google Sites. Yesterday I copied one of my functioning scripts and modified as I have done many times in the past but now the new scriptit does not run, it fails with a syntax error. Having stared at this extremely simple script for hours without a resolution I am thinking it may be a problem with Google, or some issue in my domain (Corp. domain).
function doGet() {
var t = HtmlService.createTemplateFromFile('index');
var ss = SpreadsheetApp.openById('sheet_ID');
var lrN = ss.getSheetByName('NCMR').getLastRow();
t.statArray = ss.getSheetByName('NCMR').getRange('Z3:Z'+lrN+'').getValues();
return t.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
snip from the execution transcript when I try to run:
[17-04-13 06:39:26:322 PDT] Sheet.getRange([Z3:Z23]) [0.072 seconds]
[17-04-13 06:39:26:381 PDT] Range.getValues() [0.058 seconds]
[17-04-13 06:39:26:382 PDT] Function.apply([[]]) [0 seconds]
[17-04-13 06:39:26:389 PDT] Execution failed: SyntaxError: Syntax error. (line 8, file "Code") [0.254 seconds total runtime]
Error from debugger:
We're sorry, a server error occurred. Please wait a bit and try again.
and then it has the following in the left hand pane of the debugger:
<Unknown file>null [0]
Code : doGet [8]
Any help or insight will be much appreciated. Apologies if this is a lay up and/or has already been answered.
EDIT to add html file:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<base target="_top">
</head>
<body>
<center>
<table>
<tr><td><b> Id</b></td></tr>
<?= for (var i = 0; i < statArray.length; i++) { ?>
<?= for (var j = 0; j < statArray[i].length; j++) { ?>
<tr><td><?= statArray[i] ?></td>
<?= } ?>
</tr>
<?= } ?>
</table>
</center>
</body>
</html>
For reference: The author of the question figured out the problem/syntax error
The reason for the error was a syntax error in a scriptlet used in the html i.e modify this:
<?= for (var i = 0; i < statArray.length; i++) { ?>
to a standard scriptlet tag
<? for (var i = 0; i < statArray.length; i++) { ?>
Html code should be the following:
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<base target="_top">
</head>
<body>
<center>
<table>
<tr><td><b> Id</b></td></tr>
<? for (var i = 0; i < statArray.length; i++) { ?>
<? for (var j = 0; j < statArray[i].length; j++) { ?>
<tr><td><?= statArray[i] ?></td>
<? } ?>
</tr>
<? } ?>
</table>
</center>
</body>
</html>
have you tried to use index.html
function doGet() {
var t = HtmlService.createTemplateFromFile('index.html');
var ss = SpreadsheetApp.openById('sheet_ID');
var lrN = ss.getSheetByName('NCMR').getLastRow();
t.statArray = ss.getSheetByName('NCMR').getRange('Z3:Z'+lrN+'').getValues();
return t.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}

How to put the data taken from google custom search API to checkbox list

I have a code which takes data from google custom search API, There is no wrong with the custom search API part, it retrieves data without any error
<html>
<head>
<title>JSON Custom Search API Example</title>
</head>
<body>
<div id="content"></div>
<script>
var pageName = new Array();
var pageLink = new Array();
var pageDetails = new Array();
function hndlr(response) {
for (var i = 0; i < response.items.length; i++) {
var item = response.items[i];
pageName[i] = item.title;
pageLink[i] = item.link;
pageDetails[i] = item.htmlSnippet;
}
}
// Some codes
var search_query = 'https://www.googleapis.com/customsearch/v1?key=MY_KEY&cx=XXXXXXXXX&q='+query+'&start=1&callback=hndlr';
s = document.createElement('script');
s.src = search_query;
document.getElementsByTagName('head')[0].appendChild(s);
</script>
</body>
</html>
Required data are saved in the pageName, pageLink and pageDetails arrays.
Now I want display them with chechboxes and allow user to select them.
I need to take the links of the selected sites(pageLink variable) and pass it to anothe file using POST method
I tried using bellow code just before end of the body tag()
<form action="b.php" method="post">
<script>
for (var j = 0; j < 5; j++) {
document.write("<input type='checkbox' name='formDoor[]' id='"+j+"' value= '' />"+pageName[j]+"<br />");
document.getElementById(j).value = pageLink[j];
}
</script>
<input type="submit" name="formSubmit" value="Submit" />
</form>
But in the other file, it says variables are undefined. seems like variables doesn't pass to the 'b.php' file
Can anyone please tell me how to do this?
Your current code for adding checkboxes would likely be executed before the search result arrives (i.e.: before hndlr is executed), so all arrays are still empty. The solution would be to move the checkbox creation code into the hndlr function.
Here's the fixed page.
<html>
<head>
<title>JSON Custom Search API Example</title>
</head>
<body>
<div id="content"></div>
<form id="bform" action="b.php" method="post">
<input type="submit" name="formSubmit" value="Submit" />
</form>
<script>
var pageName = new Array();
var pageLink = new Array();
var pageDetails = new Array();
function hndlr(response) {
var f=document.getElementById('bform'), prev=f.children[0];
for (var i = 0; i < response.items.length; i++) {
var ele, item = response.items[i];
pageName[i] = item.title;
pageLink[i] = item.link;
pageDetails[i] = item.htmlSnippet;
ele = document.createElement('BR');
f.insertBefore(ele, prev);
prev = ele;
ele = document.createTextNode(pageName[i]);
f.insertBefore(ele, prev);
prev = ele;
ele = document.createElement('INPUT');
ele.type = 'checkbox';
ele.name = 'formDoor[]';
ele.id = i;
ele.value = encodeURI(pageLink[i]);
f.insertBefore(ele, prev);
prev = ele;
}
}
// Some codes
var search_query = 'https://www.googleapis.com/customsearch/v1?key=MY_KEY&cx=XXXXXXXXX&q='+query+'&start=1&callback=hndlr';
s = document.createElement('script');
s.src = search_query;
document.getElementsByTagName('head')[0].appendChild(s);
</script>
</body>
</html>