AppScript - create dynamic HTML table from AppScript variable - google-apps-script

I´m trying to create an HTML table which get´s data from an array that is being passed from request.gs to template.html.
My current setup is:
request.gs (partial snippet):
var products = tempSheet.getRange("B2:B").getValues();
var template = HtmlService.createTemplateFromFile('template');
template.products = products;
var emailToSend = template.evaluate().getContent();
MailApp.sendEmail({
to: Session.getActiveUser().getEmail(),
subject: 'test mail ' + Utilities.formatDate(new Date(), "GMT+2", "dd.MM.yyyy"),
htmlBody: emailToSend
});
template.html (partial snippet):
<table>
<tr>
<th>Product Name</th>
</tr>
<tr>
<td>placeholder</td>
</tr>
</table>
I do have access to the products variable within the template.html using <?= products ?>.
Where my current knowledge and tries ended so far is how to add table rows and the data of products in each of the rows.

Here is how to add rows dynamically to the template. However I suggest you filter out blank rows first.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<table>
<tr>
<th>Product Name</th>
</tr>
<? for (var i = 0; i < products.length; i++) { ?>
<tr>
<td><?= products[i][0] ?></td>
</tr>
<? } ?>
</table>
</body>
</html>
Reference
Templated HTML

It will be something like:
<table>
<tr>
<th>Product Name</th>
</tr>
<? for (var i = 0; i < data.length; i++) { ?>
<tr>
<td><?= products[i] ?></td>
</tr>
<? } ?>
</table>

Related

Passing HTML to HTML template

I know that if we want to pass variables from .gs to the HTML template, we do this:
On .html (using <?= someVariable ?>:
<div>
<table id="summary">
<thead>
<th>Qty</th>
<th>Item</th>
<th>Price</th>
<th>Subtotal</th>
</thead>
<tbody>
// variable value goes into here
<?= items ?>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td>Total: </td>
// and into here
<td><b><?= totalCost ?></b></td>
</tr>
</tfoot>
</table>
</div>
And on .gs :
var items = holdSS.getRange(i,5).getValue()
var totalCost = holdSS.getRange(i,6).getValue()
var html = HtmlService.createTemplateFromFile("requestorEmail.html")
// matching the <?= ?> in HTML template
html.items = items
html.totalCost = totalCost
var htmlInString = html.evaluate().getContent()
The size of variable items varies, so I decided to leave it in HTML form, then pass it to the email template. So,
var items = "<tr>
<td>1</td>
<td>3M 500 Scotch Utility Trans. Tape 12mm x 25m</td>
<td>0.98</td>
<td>0.98</td>
</tr>
<tr>
<td>2</td>
<td>3M 558 Bulletin Board - Mocha</td>
<td>19.9</td>
<td>39.80</td>
</tr>
<tr>
<td>1</td>
<td>3M 560RP Post It Marker Rainbow 75mm x 12.5mm 4pad/pkt</td>
<td>3.82</td>
<td>3.82</td>
</tr>"
Resulting in (expected, at least) :
<div>
<table id="summary">
<thead>
<th>Qty</th>
<th>Item</th>
<th>Price</th>
<th>Subtotal</th>
</thead>
<tbody>
// replacing <?= items ?>
<tr>
<td>1</td>
<td>3M 500 Scotch Utility Trans. Tape 12mm x 25m</td>
<td>0.98</td>
<td>0.98</td>
</tr>
<tr>
<td>2</td>
<td>3M 558 Bulletin Board - Mocha</td>
<td>19.9</td>
<td>39.80</td>
</tr>
<tr>
<td>1</td>
<td>3M 560RP Post It Marker Rainbow 75mm x 12.5mm 4pad/pkt</td>
<td>3.82</td>
<td>3.82</td>
</tr>
</tbody>
<tfoot>
<tr>
<td></td>
<td></td>
<td>Total: </td>
// and here
<td><b><?= totalCost ?></b></td>
</tr>
</tfoot>
</table>
</div>
However, console.log(html.evaluate().getContent()) gives :
<tbody>
<tr><td>1</td><td>3M 500 Scotch Utility Trans. Tape 12mm x 25m</td><td>0.98</td><td>0.98</td>
</tr><tr><td>2</td><td>3M 558 Bulletin Board - Mocha</td><td>19.9</td><td>39.80</td></tr>
<tr><td>1</td><td>3M 560RP Post It Marker Rainbow 75mm x 12.5mm 4pad/pkt</td><td>3.82</td><td>3.82</td></tr>
</tbody>
where all the < and > in the HTML tags are escaped. I have read some threads in which they unescape the HTML entities using a textarea element but I also noted I can't execute <script> tags in email clients? What should I do in this case?
When I saw your script, it seems that HTML is put to the template HTML using <?= ... ?> of the printing scriptlets. In this case, such a result in your question is obtained.
In order to put the value as the HTML tag, please use the force-printing scriptlets like <?!= ... ?>. So, please modify it as follows.
From:
<?= items ?>
To:
<?!= items ?>
And, about <?= totalCost ?>, if you want to put the HTML tag, please modify this like <?!= totalCost ?>.
Reference:
HTML Service: Templated HTML

I'm using g-script to put entries into an HTML file and send that file as email body. But array I'm storing my values in cannot be read by HMTL file

Here is the g-script code I've written to push entires from a sheet where approval hasn't been made and send it to the person who has to make those approvals as a mail reminder.
I keep getting an error that says "Cannot read property 'vin' of undefined ", where vin is a key in the dictionary that I've pushed to the variable called 'responses_sheet'.
From there the variable is read into the HTML file and a for loop run through it, to make separate entries in the table with the headers already defined. But whenever the code reaches the HTML file it returns the error. Please help as I can't figure out the error.
function time_trig_pending() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet Name");
var n = sheet.getLastRow();
var range_ceo_app = sheet.getRange("P1:P").getValues();
var range_TAT_ceo = sheet.getRange("AA1:AA").getValues();
var range_vin = sheet.getRange("C1:C").getValues();
var range_amount_discount = sheet.getRange("K1:K").getValues();
var range_rm_name = sheet.getRange("N1:N").getValues();
var range_gm_name = sheet.getRange("M1:M").getValues();
var last_filled = 0
for (i=0;i<=n;i++){
last_filled += 1;
if (range_vin[i] == ""){
break
}
}
var responses_sheet = [];
for (i=1;i<=last_filled;i++){
if (range_ceo_app[i] == "" && range_amount_discount[i] >= 5000 ){
responses_sheet.push({
ceo_app : String(range_ceo_app[i]),
tat: parseInt(range_TAT_ceo[i]),
vin: String(range_vin[i]),
discount: parseInt(range_amount_discount[i]),
rm_name: String(range_rm_name[i]),
gm_name: String(range_gm_name[i])
});
}
}
var htmlBody = HtmlService.createTemplateFromFile('Pending Approval(html)');
htmlBody.responses = responses_sheet;
Logger.log(htmlBody.responses);
var email_html = htmlBody.evaluate().getContent();
MailApp.sendEmail(
"something#gmail.com",
"Approval Reminder Mail",
'',
{ name : "Reminder System",
htmlBody : email_html
});
}
and the HTML file I've linked to it is this :
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h3>This is to notify you of the pending approvals from your side.</h3>
<table border="2">
<caption></caption>
<tr align="center" style="background-color:rgb(77, 159, 214)">
<th scope="col">VIN</th>
<th scope="col">RM Name</th>
<th scope="col">GM Name</th>
<th scope="col">Discount</th>
<th scope="col">TAT</th>
</tr>
<tr align="center">
<? for(i=0;i<=responses.length;i++) { ?>
<td> <?= responses[i].vin ?> </td>
<td> <?= responses[i].rm_name ?> </td>
<td> <?= responses[i].gm_name ?> </td>
<td> <?= responses[i].discount ?> </td>
<td> <?= responses[i].tat ?> </td>
<? } ?>
</tr>
</body>
</html>
I had the same experience as you. In that case, I used forEach instead of the for loop. Although I'm not sure whether this same method can be used for your situation, I would like to propose the following modification.
From:
<? for(i=0;i<=responses.length;i++) { ?>
<td> <?= responses[i].vin ?> </td>
<td> <?= responses[i].rm_name ?> </td>
<td> <?= responses[i].gm_name ?> </td>
<td> <?= responses[i].discount ?> </td>
<td> <?= responses[i].tat ?> </td>
<? } ?>
To:
<? responses.forEach(e => {?>
<td> <?= e.vin ?> </td>
<td> <?= e.rm_name ?> </td>
<td> <?= e.gm_name ?> </td>
<td> <?= e.discount ?> </td>
<td> <?= e.tat ?> </td>
<? }); ?>

How can create an editable JQuery datatable

I am creating an editable JQuery data-table from the model list . I want to edit some of the column [Rate, Qty, IsBranded, Description] of each record listed in a table. My code is given below.
ProductModel
Id int
Name string
Rate decimal
Qty int
Price decimal
Description string
Html and Javascript
<script type="text/javascript">
$("document").ready(function () {
$('#tbllist').DataTable();
});
</script>
#model List<Product>
<table id="tbllist" class="cell-border" style="width:100%">
<thead class="thead-light">
<tr>
<td>Name</td>
<td>Rate</td>
<td>Qty</td>
<td>total</td>
<td>IsBranded</td>
<td>Description</td>
</tr>
</thead>
<tbody>
#if (Model != null)
{
for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>#Model[i].Name</td>
<td>#Model[i].Rate</td>
<td>#Model[i].Qty</td>
<td>#Model[i].total</td>
<td><input type="checkbox" #(Model[i].IsBranded ? "checked" : "") /></td>
<td>#Model[i].Description</td>
</tr>
}
}
</tbody>
</table>
I want to make edit Rate,Qty, Description, IsBranded column. It would be very appreciated , if someone can help me with appropriate code to make .
With Thanks
Alan
I made an example based on #StéphaneLaurent comment, hope it can work for you.
Copy the dataTables.cellEdit.js to your project, you can place it under wwwroot/js
Reference it in your page
<script src="~/js/dataTables.cellEdit.js"></script>
Then follow the tutorial.
#model List<ProductModel>
<table id="tbllist" class="cell-border" style="width:100%">
<thead class="thead-light">
<tr>
<td>Name</td>
<td>Rate</td>
<td>Qty</td>
<td>Total</td>
<td>IsBranded</td>
<td>Description</td>
</tr>
</thead>
<tbody>
#if (Model != null)
{
for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>#Model[i].Name</td>
<td>#Model[i].Rate</td>
<td>#Model[i].Qty</td>
<td>#Model[i].Total</td>
<td><input type="checkbox" #(Model[i].IsBranded ? "checked" : "") /></td>
<td>#Model[i].Description</td>
</tr>
}
}
</tbody>
</table>
#section scripts{
<script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script>
<script src="~/js/dataTables.cellEdit.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css" />
<script type="text/javascript">
var table = $('#tbllist').DataTable();
function myCallbackFunction(updatedCell, updatedRow, oldValue) {
console.log("The new value for the cell is: " + updatedCell.data());
console.log("The values for each cell in that row are: " + updatedRow.data());
}
table.MakeCellsEditable({
"onUpdate": myCallbackFunction
});
</script>
}
Result:

How to show data in html table using PHP

This is my code the table appears up with the header and everything but the row with the td tag data shows nothing at all
echo "<p></p>";
//display name of the page and some random text
echo "<h2>".$pagename."</h2>";
if($_POST['h_prodid'] > 0){
$newprodid = $_POST['h_prodid'];
$reququantity =$_POST['quantity'];
$_SESSION['basket'][$newprodid]=$reququantity;
echo "<p>Your basket has been updated</p>";
} else {
echo "<p>Existing basket</p>";
}
echo "<table border = '1'>
<tr><th>Product Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>";
echo "<td>".$reququantity."</td>";
echo "</table>";
The reason your data is NOT displaying in the html is because it is NOT in a tr ( table row ) - simply a floating table cell
if( $_SERVER['REQUEST_METHOD']=='POST' ){
echo "<h2>".$pagename."</h2>";
if( isset( $_POST['h_prodid'],$_POST['quantity'] ) ){
if( $_POST['h_prodid'] > 0 ){
$newprodid = $_POST['h_prodid'];
$reququantity =$_POST['quantity'];
$_SESSION['basket'][$newprodid]=$reququantity;
echo "<p>Your basket has been updated</p>";
}else{
echo "<p>Existing basket</p>";
}
echo "
<table border = '1'>
<tr>
<th>Product Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
<tr>
<td> </td>
<td> </td>
<td>{$reququantity}</td>
<td> </td>
</tr>
</table>";
}
}

table with an url image inside header

I have a sortable table in html and looking to put an add.png inside each table header so if a user wants to add another url - they just click the add.png and redirects them to the addurl.php (if they click outside it will still sort the table just like normal). Right now it sorting when I click on the .png and/or outside it. If this something that can't be done, I've also thought about trying to add a final row with [add another site] but have no idea how to work it into the $j++ so it appears at the final row.
what it looks like
[PHP snippet]
<table class="datatable sortable selectable full">
<thead>
<tr class="theader">
<th width="100%">
<b><li><img src="images/logos/googlebuzz-2-icon.png" height="18" border="0" />Google <img src="img/icons/add.png" height="18" border="0" align="right"/></li></b>
</th>
<th>
<b>Coins</b>
</th>
<th>
<b>CPC</b>
</th>
</tr>
</thead>
<tbody>
<?
$site2 = mysql_query("SELECT * FROM `sites` WHERE `user`='{$data->login}' ORDER BY `cpc` DESC");
for($j=1; $site = mysql_fetch_object($site2); $j++)
{
?>
<tr>
<td>
<? if($site->banned == 1){ ?><font color="red"><? }else{ ?><font color="green"><? } echo($site->title); ?></font><span class="edit"></span>
</td>
<td align="right">
<? if($site->points <= 10){ ?><font color="red"><? }else{ ?><font color="green"><? } echo($site->points); ?></font><span class="add"></span>
</td>
<td>
<? echo $site->cpc;?>
</td>
</tr>
<?}?>
</tbody>
</table>
First of all, instead of a for loop, you should use while:
while($site = mysql_fetch_object($site2))
If you want to add a final row, just add it outside the iteration, before </tbody>:
<tbody>
<? while($site = mysql_fetch_object($site2)): ?>
<tr>
<td>
<? if($site->banned == 1){ ?><font color="red"><? }else{ ?><font color="green"><? } echo($site->title); ?></font><span class="edit"></span>
</td>
<td align="right">
<? if($site->points <= 10){ ?><font color="red"><? }else{ ?><font color="green"><? } echo($site->points); ?></font><span class="add"></span>
</td>
<td>
<? echo $site->cpc;?>
</td>
</tr>
<? endwhile; ?>
<tr>
<td colspan="3">
[add another site]
</td>
</tr>
</tbody>
It seems you are using a javascript to do the sorting. I don't know if that can be changed so your add button in the header works.
You can check if it's the final row by checking the actual row against all rows:
$site2 = mysql_query("SELECT * FROM `sites` WHERE `user`='{$data->login}' ORDER BY `cpc` DESC");
$rows = mysql_num_rows($site2);
$i = 0;
And add this in your for loop:
$i++;
if($rows == $i) {
// do something only at the final row
}