2

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?

5
  • like print preview you need CSV preview? Commented Nov 18, 2019 at 10:19
  • Thanks for the response! - In my HTML component I want to be able to display how that CSV would look before it's downloaded. I'm taking a guess that maybe that's converted into a HTML table or as you said, as CSV from a string? Commented Nov 18, 2019 at 10:21
  • you can use split() to separate the words by the coma and load the data HTML Table Commented Nov 18, 2019 at 10:25
  • Thanks Hiras - Do you have an example of how to achieve that? Commented Nov 18, 2019 at 10:29
  • 1
    tutorialrepublic.com/… take a look at this code snippet Commented Nov 18, 2019 at 10:32

2 Answers 2

7

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>
Sign up to request clarification or add additional context in comments.

11 Comments

Damn, this is some type of wizardry you're performing here. Amazing. It's so close. The only error I have is that if I console log the data, everything appears fine. But in the HTML, one of the values in the row has moved left a few spaces and is appearing about 6 cells before it's supposed to.
Thank you :-) Do you mind sharing your csv data so I can see the error and help solve it?
So the console log correct logs the order as follows: "20", "Order", " Item", " Title", " Assignee ID", " Assignee", " Comments", " Image Path", " Timestamp", " Due Date", " Completed" these "act" as headers. The values below correctly match up to each of these "headers" "21", "0", "1", ""Issue 1"", ""Not done yet"", ""James"", ""Comment"", "", ""18/11/2019 16:28:49"", ""18/11/2019 16:28:49"", ""true"". However, int he HTML, the "Completed" value appears 3rd, opposed to last.
I changed the solution a bit to support: 1. unsorted object attributes, 2. commas inside quotes
Let me know if my latest change fixed your problem
|
1

Your question actually mixes two concerns,

  1. parsing the csv and
  2. presenting its data.

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!

1 Comment

In general, I agree that for more complex cases, a well tested and well-used library is better. But since this is a string the OP creates himself in his typescript code, it sounds to me like an overkill solution for this use case. when you parse a file you created, you probably can write something much smaller and efficient for the specific case you implemented.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.