I have a string that I've built in my Typescript file that's comma separated. This can be exported as a 'file.csv' and everything is displayed correctly once downloaded.
What I would like to achieve is creating a "preview" of this string before it's downloaded. I would like the preview to resemble that of a HTML Table, or how it would appear in a CSV.
Example String
1,Header1, Header2, Header3, Header4,
2,0,1,"Content","More Content","
Naturally, in a CSV this would appear the same as the above but separated within borders / cells.
Is it possible to achieve this in HTML?
Here's an example I created on stackblitz: https://stackblitz.com/edit/angular-k162aa
The main CSV parsing function is described below with inline comments:
// CSV is assumed to have headers as well
csvToJSON(csv: string) {
const lines: string[] = csv
// escape everything inside quotes to NOT remove the comma there
.replace(/"(.*?)"/gm, (item) => encodeURIComponent(item))
// split by lines
.split('\n');
// separate the headers from the other lines and split them
const headers: string[] = lines.shift().split(',');
// should contain all CSV lines parsed for the html table
const data: any[] = lines.map((lineString, index) => {
const lineObj = {};
const lineValues = lineString.split(',');
headers.forEach((valueName, index) => {
// remove trailing spaces and quotes
lineObj[valueName] = lineValues[index]
// handle quotes
.replace(/%22(.*?)%22/gm, (item) => decodeURIComponent(item))
// trim trailing spaces
.trim();
})
return lineObj; // return lineValues for array representation.
});
return { data, headers };
}
csvToJSON(csv: string) {
const lines: string[] = csv.split('\n');
// separate the headers from the other lines and split them
const headers: string[] = lines.shift().split(',');
// should contain all CSV lines parsed for the html table
const data: string[][] = lines.map((lineString, index) => {
const lineObj = {};
const lineValues = lineString.split(',');
headers.forEach((valueName, index) => {
lineObj[valueName] = lineValues[index];
});
return lineObj; // return lineValues for an array.
});
return { data, headers };
}
Notice that the commented code can give you an array of arrays, while the code as is, returns an array of objects.
In the HTML, this format is easier to render since the index of the headers is the same as the index of each item array:
<table class="table">
<thead>
<tr>
<th *ngFor="let header of headers">{{ header }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of data">
<td *ngFor="let header of headers">
{{ row[header] }}
</td>
</tr>
</tbody>
</table>
To support row headers, you can use the following html snippet for the table body section:
<tr *ngFor="let row of data">
<ng-container *ngFor="let attribute of row; let i = index">
<td *ngIf="i">{{ attribute }}</td>
<th *ngIf="!i">{{ attribute }}</th>
</ng-container>
</tr>
Your question actually mixes two concerns,
Each of the above tasks has its own pitfalls and while it is quite easy to create a simple parser and rendering code it is much easier to avoid future pain by using third party code specialized in these tasks. If you choose well (yes, there are plenty of options) you won't have to worry about covering boundary conditions, size limitations, performance bottlenecks, error handling, XSS vulnerabilities (injected from the data but you should assess library vulnerabilities).
If it was my project I would go with something like Papa Parse and DataTables for parsing and presentation. Check DataTables csv import example page: https://editor.datatables.net/examples/extensions/import
If, on the other hand, you want the challenge to work on all these and you are sure the data will always behave then by all means write you own code!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With