I have an excel spreadsheet template. I need to create excel file from data in html/php page. That is, the data on the html page is scattered throughout the page, and I need to somehow collect all this data (through classes or ids) and place it in certain cells of the table.
I tried a lot of ready-made solutions, for example https://github.com/jmaister/excellentexport/tree/master/test
<html>
<head>
<script type="text/javascript" src="https://unpkg.com/xlsx#0.15.1/dist/xlsx.full.min.js"></script>
</head>
<body>
<table id="tbl_exporttable_to_xls" border="1">
<thead>
<th>Sr</th>
<th>Name</th>
<th>Location</th>
<th>Job Profile</th>
</thead>
<tbody>
<tr>
<td>1</td>
<td>
<p>Amit Sarna</p>
</td>
<td>Florida</td>
<td>Data Scientist</td>
</tr>
<tr>
<td>2</td>
<td>
<p>Sagar Gada</p>
</td>
<td>California</td>
<td>Sr FullStack Dev</td>
</tr>
<tr>
<td>3</td>
<td>
<p>Ricky Marck</p>
</td>
<td>Nevada</td>
<td>Sr .Net Dev</td>
</tr>
<tr>
<td>4</td>
<td>
<p>Andrea Ely</p>
</td>
<td>New York</td>
<td>Sr Delivery Mgr</td>
</tr>
<tr>
<td>5</td>
<td>
<p>Sunita Devgan</p>
</td>
<td>Colorado</td>
<td>UiUx Expert</td>
</tr>
</tbody>
</table>
<button onclick="ExportToExcel('xlsx')">Export table to excel</button>
<script>
function ExportToExcel(type, fn, dl) {
var elt = document.getElementById('tbl_exporttable_to_xls');
var wb = XLSX.utils.table_to_book(elt, { sheet: "sheet1" });
return dl ?
XLSX.write(wb, { bookType: type, bookSST: true, type: 'base64' }) :
XLSX.writeFile(wb, fn || ('MySheetName.' + (type || 'xlsx')));
}
</script>
</body>
</html>
Only import from html table is used everywhere.
Sorry for my English, so I'm attaching images for more details.
enter image description here
Tried options
https://codepedia.info/javascript-export-html-table-data-to-excel
https://github.com/jmaister/excellentexport/tree/master/test
https://www.webslesson.info/2021/07/how-to-display-excel-data-in-html-table.html
https://codepedia.info/javascript-export-html-table-data-to-excel
but everywhere there is an import only by html table
Related
I am a bit stuck when creating the PDF from the html template. if i use the static entries it does work but i am looking to generate the pdf for multiple items and the items can vary each time, i am not sure if i am missing something any suggestion guys please.
after i added .map function in the pdfTemplate.js file as below to go through each object and get the price, it seems to not working properly
snippet from the main ./documents/pdfTemplate Js file
customer_dataand_Itemsbought.map((eachitem) => {
total = total + eachitem.price;
return ` <tr class="heading">
<td>Bought items:</td>
<td>Price</td>
</tr>
<tr class="item">
<td>First item:</td>
<td>${eachitem.price}$</td>
</tr>
<tr class="item">
<td>Second item:</td>
<td>${eachitem.price}$</td>
</tr>`;
})`
Note: PDF is still generating but missing every bit that i included after the .map function
main ./documents/pdfTemplateJs file
module.exports = (customer_dataand_Itemsbought) => {
//customer_dataand_Itemsbought is array of objects[obj[0],obj[1],...] and at obj[0] is the customer details object and after that each index object contains the customer bought items detail/price
let customer_Name = customer_dataand_Itemsbought[0].user_FirstName;
let receipt_Id = customer_dataand_Itemsbought[0].Invoice_No_latest;
let total_Price;
customer_dataand_Itemsbought.shift();
const today = new Date();
return `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PDF Result Template</title>
<style>
........
</style>
</head>
<body>
<div class="invoice-box">
<table cellpadding="0" cellspacing="0">
<tr class="top">
<td colspan="2">
<table>
<tr>
<td class="title"><img src="https://i2.wp.com/cleverlogos.co/wp-content/uploads/2018/05/reciepthound_1.jpg?fit=800%2C600&ssl=1"
style="width:100%; max-width:156px;"></td>
<td>
Datum: ${`${today.getDate()}. ${today.getMonth() + 1}. ${today.getFullYear()}.`}
</td>
</tr>
</table>
</td>
</tr>
<tr class="information">
<td colspan="2">
<table>
<tr>
<td>
Customer name: ${customer_Name} // this still appears on the PDF
</td>
<td>
Receipt number: ${receipt_Id} // this still appears on the PDF
</td>
</tr>
</table>
</td>
</tr>`;
// here i used the .map function to go through each array object. nothing appears from here onwards on the pdf
customer_dataand_Itemsbought.map((eachitem) => {
total = total + eachitem.price;
return ` <tr class="heading">
<td>Bought items:</td>
<td>Price</td>
</tr>
<tr class="item">
<td>First item:</td>
<td>${eachitem.price}$</td>
</tr>
<tr class="item">
<td>Second item:</td>
<td>${eachitem.price}$</td>
</tr>`;
})`
</table>
<br />
<h1 class="justify-center">Total price: ${total_Price}£</h1>
</div>
</body>
</html>
`;
};
at server.js side
const pdf = require("html-pdf");
const pdfTemplate = require("./documents/pdfTemplate");
pdf.create(pdfTemplate(customer_dataand_Itemsbought), {}).toFile("./filename.pdf", function (err, res) {
if (err) {
console.log(err);
}
});
When you have enclosed map function's body in curly braces - meaning you have multiple statements for processing data, you are expected to return from it explicitly:
customer_dataand_Itemsbought.map((eachitem) => {
total = total + eachitem.price;
return `<tr class="heading">
<td>Bought items:</td>
<td>Price</td>
</tr>
<tr class="item">
<td>First item:</td>
<td>${eachitem.price}$</td>
</tr>
<tr class="item">
<td>Second item:</td>
<td>${eachitem.price}$</td>
</tr>`;
})
I have 2 pages and 2 tables, in page 1(table 1) I want to send selected rows to page 2(table 2) where in table 2 I show the selected rows
This is the first table in page 1:
<table class="src-table">
<tr>
<th>Select</th>
<th>Firstname</th>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>Jill</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>Eve</td>
</tr>
</table>
<br>
<input type="button" value="Submit" id="submit">
Like image below
This is the second table in page 2:
<table class="target-table">
<tr>
<th>Select</th>
<th>Firstname</th>
</tr>
</table>
Like image below
If you really need this. You can use localStorage.
localStorage not working in the sandbox. But you can use it your application as well.
run storeItems when you need to save selected to store (for example on element select)
run appendStoredToAnouther on window event window.onload on page with the target table
function storeItems() {
const selectedItems = document.querySelectorAll("#first-table .selected");
const selectedHtml = nodeListToString(selectedItems);
localStorage.add('selectedTableItems', selectedHtml);
}
function nodeListToString(nodeList) {
let string = '';
nodeList.forEach(function(node) {
string += node.outerHTML;
});
return string;
}
function appendStoredToAnouther() {
const itemsHtml = window.localStorage.get('selectedTableItems');
const targetTable = document.getElementById('target-table');
targetTable.innerHTML = itemsHtml + targetTable.innerHTML;
}
<table id="first-table">
<tr class="selected">
<td>1</td>
<td>Selected</td>
<td>Item</td>
</tr>
<tr class="selected">
<td>1</td>
<td>Selected</td>
<td>Item</td>
</tr>
<tr>
<td>2</td>
<td>Not Selected</td>
<td>Item</td>
</tr>
</table>
<button type="button" onclick="storeItems()">Send to anouther</button>
<button type="button" onclick="appendStoredToAnouther()">Append stored to anouther</button>
<table id="target-table">
<tr class="selected">
<td>1</td>
<td>Selected</td>
<td>Item</td>
</tr>
<tr>
<td>2</td>
<td>Not Selected</td>
<td>Item</td>
</tr>
</table>
Below I demonstrated how some lines could be carried over from one table to another one on a following page. However, since both pages are probably hosted on the same server it is in most cases more practical to first collect some unique identifiers for the selected table records, transmit them to the next page and then get the actual table contents from the original data source again (in many cases a database table or view). This approach will also make your page safer against unauthorised injections.
In case that the tables are to be shown in two consecutive pages you can do the following:
// shortcut for utility function querySelectorAll():
const qsa=(s,o)=>[...(o||document)['querySelectorAll'](s)];
const log=qsa('#log')[0];
qsa('#submit')[0].addEventListener('click',function(){
var dat="tbl="+JSON.stringify(
qsa('tr',qsa('.src-table')[0]).filter(tr=>qsa('input:checked',tr).length)
.map(tr=>qsa('td',tr).slice(1)
.map(td=>td.innerHTML))
);
log.innerHTML+="<hr>dat:"+dat;
log.innerHTML+="\nwindow.location=\"page2.html?\"+encodeURIComponent(dat)";
// this second part would be placed in the onload section if the next page:
log.innerHTML+='var dat=window.location.search.substr(1)'
var args=dat.split("&").reduce((a,v)=>{
var t=v.split('=');
a[t[0]]=JSON.parse(decodeURIComponent(t[1]));
return a;},
{}
);
qsa('.trg-table')[0].innerHTML+=
args.tbl.map((r,i)=>'<tr><td>'+(i+1)+'</td><td>'+r.join('</td><td>')+'</td></tr>').join('');
})
<h2>page one</h2>
<table class="src-table">
<tr><th>Select</th><th>Firstname</th><th>Familyname</th></tr>
<tr><td><input type="checkbox"></td><td>Jill</td><td>Jones</td></tr>
<tr><td><input type="checkbox"></td><td>Eve</td><td>Adams</td></tr>
</table>
<br>
<input type="button" value="Submit" id="submit">
<h2>this would be the second page</h2>
<table class="trg-table">
<tr><th>no</th><th>Firstname</th><th>Familyname</th></tr>
</table>
<pre id="log"></pre>
As this is a sandbox the last lines had to modified a bit. In your page you should actually redirect your page with the window.location assignment.
On the second page you then need to read the passed information from window.location.search and use that information to append it to your table there.
I am using Datatables to create some tables with data. I am using the option they have to include a download CSV button. However, since I am working with large amounts of data, I wanted to put a "Loading, please wait..." gif on the screen so users know it is downloading. However, the gif loading I have requires that I activate it before the downloading starts, and deactivate it after the downloading finishes (really it's just adding a class to the body which makes it a bit transparent and adds a wheel gif). I haven't found a way to run code before and after download. I think I can do before with action, but not after.
Can anyone help me out with this? Is there a way to do code before download and after download? Or, in absence of that, is there maybe a better way I can implement a waiting gif while it prepares the csv for download?
You can extend the csv-button and call the original action inside it. This enables you to show a custom loading-indicator while the export is being processed.
$(document).ready(function() {
let table = $('#example').DataTable({
processing: true,
dom: 'Bfrtip',
buttons: [{
extend: 'csv',
text: 'CSV Export',
action: function(e, dt, button, config) {
console.log('csv button clicked');
this.processing(true); // show indicator on button
console.log('before csv export');
config.filename = 'my-export'; // set filename
$.fn.dataTable.ext.buttons.csvHtml5.action.call(this, e, dt, button, config); // call export-action
console.log('after csv export');
this.processing(false); // hide indicator on button
}
}],
});
});
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/jq-3.3.1/dt-1.10.18/b-1.5.6/b-html5-1.5.6/datatables.min.css" />
<script type="text/javascript" src="https://cdn.datatables.net/v/dt/jq-3.3.1/dt-1.10.18/b-1.5.6/b-html5-1.5.6/datatables.min.js"></script>
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Test-1</td>
<td>Test 1234</td>
</tr>
<tr>
<td>Test-3</td>
<td>I dont know!</td>
</tr>
<tr>
<td>Test-2</td>
<td>Another...</td>
</tr>
<tr>
<td>Alpha-1</td>
<td>Apple Test</td>
</tr>
<tr>
<td>Alpha-2</td>
<td>Banana Test</td>
</tr>
<tr>
<td>Alpha-3</td>
<td>Coconut</td>
</tr>
<tr>
<td>Beta-1</td>
<td>Beta</td>
</tr>
<tr>
<td>Beta-2</td>
<td>Not Alpha</td>
</tr>
<tr>
<td>Alpha-1</td>
<td>Apple Test</td>
</tr>
<tr>
<td>Alpha-2</td>
<td>Banana Test</td>
</tr>
<tr>
<td>Alpha-3</td>
<td>Coconut</td>
</tr>
<tr>
<td>Beta-1</td>
<td>Beta</td>
</tr>
<tr>
<td>Beta-2</td>
<td>Not Alpha</td>
</tr>
</tbody>
</table>
I have a table structure like this. Fairly simple one.
<table id="myTable" class="table">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
<tr>
<td>Mary</td>
<td>Moe</td>
<td>mary#example.com</td>
</tr>
<tr>
<td>July</td>
<td>Dooley</td>
<td>july#example.com</td>
</tr>
</tbody>
</table>
At runtime I am binding a new row to this table for a particular rowclick. This new row contains a new table.
Now on clicking the row again, I want to be able to remove the newly added row(the new table).
I am using bootstrap table.
Here is what I have tried so far.
$('#myTable').on('click-row.bs.table', function (e, row, $element) {
//if ($element.has('#newlyAddedTable').length) { ....// did not work
if ($('#myTable').has('#newlyAddedTable').length) { // this removes the table on any row click. Not what I intend to do
{
$("#newlyAddedTable").remove();
} else {
// some operation...
}
}
I want to be able to remove the newly added table on the row it was created.
Just more explanation based on the Answers below:
<tr> ----------> if i click this
<td>
<table id="newlyAddedTable"> ---------> this is added
</table>
</td>
</tr>
<tr> ----------> if i again click this or maybe any other row in the table
<td>
<table id="newlyAddedTable"> ---------> this is removed
</table>
</td>
</tr>
Update: from OP's comment below it sounds like the best way to implement the new table is to use a class selector and not an id selector. The code below has been updated accordingly. ***Where previously there was an id for newTable there is a class ---> #newTable ===> .newTable:
Just change:
$('#myTable').has('#newlyAddedTable').length
To:
$('.newlyAddedTable', $element).length //element === clicked row -- see demo
vvvvv DEMO vvvvv
$('#myTable').bootstrapTable().on('click-row.bs.table', function(e, row, $element) {
if( $('.newTable', $element).length ) {
$('.newTable', $element).remove();
} else {
$('td:first', $element)
.append( '<table class="newTable"><tr><td>NEW TABLE</td></tr></table>' );
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.7.0/bootstrap-table.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.7.0/bootstrap-table.min.css" rel="stylesheet"/>
<table id="myTable" class="table">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
<tr>
<td>Mary</td>
<td>Moe</td>
<td>mary#example.com</td>
</tr>
<tr>
<td>July</td>
<td>Dooley</td>
<td>july#example.com</td>
</tr>
</tbody>
</table>
Try replacing your remove code with this:
$(document).on("click", "#newlyAddedTable", function(){
$(this).remove();
});
The code above registers a click listener on the document. The second parameter filters those events for those with the target #newlyAddedTable. This way you don't have to register a new click handler every time you insert a row (as in #VimalanJayaGanesh's solution).
P.S. If you are adding HTML that looks like this:
<tr>
<td>
<table id="newlyAddedTable">
</table>
</td>
</tr>
Then you are probably actually wanting to remove the parent tr (not the table with the id). There are two ways to fix this.
You can change the selector that filters click events and so have the tr handle the click rather than the table element in my example code:
$(document).on("click", "tr:has(#newlyAddedTable)", function(){
You can leave the selector as is but grab the parent tr from the table and remove that changing the remove line above to:
$(this).parents("tr").first().remove()
or
$(this).parent().parent().remove()
As I don't have your complete code / fiddler, here is a possible solution.
Are you looking for something like this?
$('#add').on('click', function()
{
var newRow = '<tr CLASS="newrow"><td colspan="3"><table><tr><td>Test</td><td>User</td><td>test#example.com</td></table></td></tr>'
$('#myTableBody').append(newRow);
Remove()
});
function Remove()
{
$('.newrow').off('click').on('click', function()
{
$(this).remove();
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="myTable" class="table">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
</tr>
</thead>
<tbody id="myTableBody">
<tr>
<td>John</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
<tr>
<td>Mary</td>
<td>Moe</td>
<td>mary#example.com</td>
</tr>
<tr>
<td>July</td>
<td>Dooley</td>
<td>july#example.com</td>
</tr>
</tbody>
</table>
<button type='button' id='add'>Add</button>
Note:
The following line indicates that,
$('.newrow').off('click').on('click', function()
the click event will be binded to the new row only once.
The reason for adding 'off('click') is, when you are dynamically adding rows (with common class 'newrow') to the table, the events will be binded several times. To avoid that, remove the previously binded click event and add a new one.
<html>
<body>
<div>
<table border="1" id="topTable">
<thead>
<th>Item</th>
<th>Sold</th>
</thead>
<tbody id="topTableBody">
<tr>
<td>Apples</td>
<td>50</td>
</tr>
<tr>
<td>Apples</td>
<td>25</td>
</tr>
<tr>
<td>Oranges</td>
<td>30</td>
</tr>
<tr>
<td>Strawberry</td>
<td>60</td>
</tr>
<tr>
<td>Cherry</td>
<td>10</td>
</tr>
<tr>
<td>Guava</td>
<td>5</td>
</tr>
<tr>
<td>Strawberry</td>
<td>20</td>
</tr>
</tbody>
</table>
</div>
<button id="btn">Click</button>
</br>
<div>
<table border="1" id="bottomTable">
<thead>
<th>Item</th>
<th>Sold</th>
</thead>
<tbody id="bottomTableBody">
</tbody>
</table>
</div>
</body>
</html>
When I press on the button I want it to loop through the top table and get the item names that're alike and add them in one row with the sold amount combined in the bottom table ex: apples will have their own row with a sold amount of 75 and others who have no names that're alike will have their own row such as Oranges with the sold amount also.
If you can use JQuery.
(JSFiddle: http://jsfiddle.net/inanda/o9axgkaz/):
jQuery('#btn').on('click', function() {
var sumMap = {};
//Iterate through table rows
$("table tbody tr").each(function () {
if (sumMap[$(this).children('td:nth-child(1)').text()]) {
sumMap[$(this).children('td:nth-child(1)').text()] = sumMap[$(this).children('td:nth-child(1)').text()] +Number($(this).children('td:nth-child(2)').text());
} else {
sumMap[$(this).children('td:nth-child(1)').text()] = Number($(this).children('td:nth-child(2)').text());
}
})
//Append result to the other table
$.each(sumMap, function (i, val) {
$('#bottomTable tr:last').after('<tr><td>'+i+'</td><td>'+val+'</td>');
});
});
Pure javascript:
(JSFiddle: http://jsfiddle.net/inanda/2dmwudfj/ ):
appendResultToBottomTable= function() {
var sumMap = calculate();
appendResultToTable('bottomTableBody', sumMap);
}
function calculate() {
var table = document.getElementById("topTableBody");
var map = {};
for (var i = 0, row; row = table.rows[i]; i++) {
var itemType=(row.cells[0].innerText || row.cells[0].textContent);
var value=(row.cells[1].innerText || row.cells[1].textContent);
if (map[itemType]) {
map[itemType] = map[itemType] +Number(value);
} else {
map[itemType] = Number(value);
}
}
return map;
}
function appendResultToTable(tableId, sumMap){
var table = document.getElementById(tableId);
for (var item in sumMap){
var row = table.insertRow(table.rows.length);
var cellItem = row.insertCell(0);
var cellValue = row.insertCell(1);
cellItem.appendChild(document.createTextNode(item));
cellValue.appendChild(document.createTextNode(sumMap[item]));
}
}
If it is applicable for your project to use external libraries, you can do it with code like below:
alasql('SELECT Item,SUM(CONVERT(INT,Sold)) AS Sold \
INTO HTML("#res",{headers:true}) \
FROM HTML("#topTable",{headers:true}) \
GROUP BY Item');
Here:
SELECT Item, SUM(Sold) FROM data GROUP BY Item is a regular SQL expression to group and sum data from the table
CONVERT(INT,Sold) conversion procedure from string to INT type
FROM HTML() and INTO HTML() special functions to read/write data from/to HTML table, {headers:true} is a parameter to use headers
I added some minor CSS code (for table and cells borders), because Alasql generates the "plain" HTML table.
See the working snippet below.
(Disclaimer: I am an author of Alasql library)
function run() {
alasql('SELECT Item,SUM(CONVERT(INT,Sold)) AS Sold INTO HTML("#res",{headers:true}) FROM HTML("#topTable",{headers:true}) GROUP BY Item');
}
#res table {
border:1px solid black;
}
#res table td, th{
border:1px solid black;
}
<script src="http://alasql.org/console/alasql.min.js"> </script>
<div>
<table border="1" id="topTable">
<thead>
<th>Item</th>
<th>Sold</th>
</thead>
<tbody id="topTableBody">
<tr>
<td>Apples</td>
<td>50</td>
</tr>
<tr>
<td>Apples</td>
<td>25</td>
</tr>
<tr>
<td>Oranges</td>
<td>30</td>
</tr>
<tr>
<td>Strawberry</td>
<td>60</td>
</tr>
<tr>
<td>Cherry</td>
<td>10</td>
</tr>
<tr>
<td>Guava</td>
<td>5</td>
</tr>
<tr>
<td>Strawberry</td>
<td>20</td>
</tr>
</tbody>
</table>
</div>
<button id="btn" onclick="run()">Click</button>
</br>
<div id="res"></div>