I’m trying to add a download button to convert table in CSV format.
PS: Most of the StackOverflow answers regarding this topic are several years old.
Here’s my Angular 17 table code in HTML which is displaying the data in table format:
<button (click)="exportcsv()" class="bnt btn-primary">Download</button>
<div class="table-responsive">
<table class="table" id="htmlData">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Date</th>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Amount</th>
<th scope="col">Mode</th>
</tr>
</thead>
<tbody class="table-group-divider">
<tr *ngFor="let item of income; let count = index;">
<th scope="row">{{count +1}}</th>
<td>{{item.date | date: "dd MMM, yyyy '@' hh:mm a"}}</td>
<td>{{item.registration.RegistrationId}}</td>
<td>{{(item.registration.fullName}}</td>
<td>{{item.totalAmount}}</td>
<td>{{item.paymentMode}}</td>
</tr>
</tbody>
</table>
</div>
This is my .TS code but it’s throwing an error while compiling:
exportcsv(): void {
let csv='';
let table=document.getElementById("htmlData");
let tr=table.children[0].children[0];
for(let i=0;i<tr.children.length;i++)
{
csv+=tr.children[i].innerText+",";
}
csv=csv.substring(0,csv.length-1)+"n";
let tbody=table.children[1];
for(let i=0;i<tbody.children.length;i++)
{
for(let j=0;j<tbody.children[i].children.length;j++)
{
csv+=tbody.children[i].children[j].innerText+",";
}
csv=csv.substring(0,csv.length-1)+"n";
}
csv=csv.substring(0,csv.length-1)+"n";
let hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
hiddenElement.target = '_blank';
hiddenElement.download = 'data.csv';
hiddenElement.click();
}
Error when compiling using ng serve
:
Error: src/app/income.component.ts:74:12 - error TS18047: 'table' is possibly 'null'.
74 let tr=table.children[0].children[0];
~~~~~
Error: src/app/income.component.ts:77:29 - error TS2339: Property 'innerText' does not exist on type 'Element'.
77 csv+=tr.children[i].innerText+",";
~~~~~~~~~
Error: src/app/income.component.ts:80:15 - error TS18047: 'table' is possibly 'null'.
80 let tbody=table.children[1];
~~~~~
Error: src/app/income.component.ts:85:48 - error TS2339: Property 'innerText' does not exist on type 'Element'.
85 csv+=tbody.children[i].children[j].innerText+",";
Using document.getElementById() can be undefined.
You could try to use a template variable for example:
<table #htmlData> ... </table>
and in your component.ts use a signal query like:
myTable = viewChild.required("htmlData");
Then you could use this variable in your Method like this:
this.myTable().children[...]
The subsequent errors should then also disappear.
You can annotate the type after document.getElementById
I have also changed innerText to nodeValue
exportcsv(): void {
let csv = '';
const table = document.getElementById("htmlData") as HTMLTableElement;
const tr = table.children[0].children[0];
for(let i=0; i < tr.children.length; i++) {
csv += tr.children[i].innerText+",";
}
csv = csv.substring(0,csv.length-1) + "n";
const tbody = table.children[1];
for(let i = 0; i < tbody.children.length; i++) {
for(let j = 0; j < tbody.children[i].children.length; j++) {
csv += tbody.children[i].children[j].innerText+",";
}
csv = csv.substring(0,csv.length-1) + "n";
}
csv = csv.substring(0,csv.length-1)+"n";
const hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
hiddenElement.target = '_blank';
hiddenElement.download = 'data.csv';
hiddenElement.click();
}