54

I am beginner in angular and I am working on Angular 5, Node v8.11.3.

I want to realize a generic function that takes in parameter data and headers. And as output a csv file.

I create a component called ' FactureComponent ' Then I generate a service called ' DataService ' then I create a getFactures function that retrieves a list of my items from a mock and it works very well.

import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';
import { FACTURES } from '../mock.factures';

@Component({
selector: 'app-facture',
templateUrl: './facture.component.html',
styleUrls: ['./facture.component.scss']
})
export class FactureComponent implements OnInit {

factures = [];
columns  = ["Id","Reference","Quantite","Prix Unitaire"];
btnText:  String = "Export CSV";

constructor(private _data: DataService) { }

ngOnInit() {
this.getFactures();
}
getFactures(){
this.factures=this._data.getFactures();
}
generateCSV(){
console.log("generate");
}
}

you will find below the view

<form>
<input type="submit" [value]="btnText" (click)="generateCSV()"/>
</form>

<table>
 <tr>
   <th *ngFor="let col of columns">
      {{col}}
   </th>
 </tr>
 <tr *ngFor="let facture of factures">
  <td>{{facture.id}}</td>     
  <td>{{facture.ref}}</td>
  <td>{{facture.quantite}}</td>
  <td>{{facture.prixUnitaire}}</td>
 </tr>
</table>

So I want to realize a function that converts my data displayed on the view into a csv file.

6
  • This is how you would do it in JavaScript, through some tinkering it should be easy to make it work in TypeScript stackoverflow.com/questions/8847766/… Commented Jul 23, 2018 at 21:59
  • The only part that may be different is the fs.WriteFile Commented Jul 23, 2018 at 22:00
  • @BradenBrown thank u for ur reply. we can't do it without using javascript ? Commented Jul 23, 2018 at 22:11
  • Do you just want to download the csv? Or save it to a local file? Commented Jul 23, 2018 at 22:13
  • @BradenBrown just download the csv Commented Jul 23, 2018 at 22:16

3 Answers 3

115

Update: Here is slightly better way to do it:

  1. Open up command prompt in the directory of your project.
  2. Install file-saver by typing npm install --save file-saver
  3. import { saveAs } from 'file-saver'; into your .ts file.
  4. Here is the updated code based on the new import.
downloadFile(data: any) {
    const replacer = (key, value) => value === null ? '' : value; // specify how you want to handle null values here
    const header = Object.keys(data[0]);
    let csv = data.map(row => header.map(fieldName => JSON.stringify(row[fieldName], replacer)).join(','));
    csv.unshift(header.join(','));
    let csvArray = csv.join('\r\n');

    var blob = new Blob([csvArray], {type: 'text/csv' })
    saveAs(blob, "myFile.csv");
}

Credits to this answer for converting an object to CSV.

Here is the method to use:

downloadFile(data: any) {
  const replacer = (key, value) => (value === null ? '' : value); // specify how you want to handle null values here
  const header = Object.keys(data[0]);
  const csv = data.map((row) =>
    header
      .map((fieldName) => JSON.stringify(row[fieldName], replacer))
      .join(',')
  );
  csv.unshift(header.join(','));
  const csvArray = csv.join('\r\n');

  const a = document.createElement('a');
  const blob = new Blob([csvArray], { type: 'text/csv' });
  const url = window.URL.createObjectURL(blob);

  a.href = url;
  a.download = 'myFile.csv';
  a.click();
  window.URL.revokeObjectURL(url);
  a.remove();
}

I'll add on later if I found a better way to do it.

Sign up to request clarification or add additional context in comments.

6 Comments

For *.ts files, add npm install @types/file-saver --save-dev
npm install @types/file-saver --save-dev
Ref URL -- Additionally, TypeScript definitions can be installed via: npm install @types/file-saver --save-dev
import method has been changed now - import { saveAs } from 'file-saver';
You can make that null check a lot easier: (key: string, value: any) => value ?? '';
|
18

My solution currently is making a service for the saving (I got this from Changhui Xu @ codeburst). No package installs needed for this...

import { Injectable } from '@angular/core';

declare global {
    interface Navigator {
        msSaveBlob?: (blob: any, defaultName?: string) => boolean
    }
}

@Injectable({
    providedIn: 'root',
})
export class CsvDataService {
    exportToCsv(filename: string, rows: object[]) {
      if (!rows || !rows.length) {
        return;
      }
      const separator = ',';
      const keys = Object.keys(rows[0]);
      const csvContent =
        keys.join(separator) +
        '\n' +
        rows.map(row => {
          return keys.map(k => {
            let cell = row[k] === null || row[k] === undefined ? '' : row[k];
            cell = cell instanceof Date
              ? cell.toLocaleString()
              : cell.toString().replace(/"/g, '""');
            if (cell.search(/("|,|\n)/g) >= 0) {
              cell = `"${cell}"`;
            }
            return cell;
          }).join(separator);
        }).join('\n');
  
      const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
      if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
      } else {
        const link = document.createElement('a');
        if (link.download !== undefined) {
          // Browsers that support HTML5 download attribute
          const url = URL.createObjectURL(blob);
          link.setAttribute('href', url);
          link.setAttribute('download', filename);
          link.style.visibility = 'hidden';
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
        }
      }
    }
  }

And then I inject this service in my component. It then calls this service:


  constructor(private csvService :CsvDataService) {}

  saveAsCSV() {
    if(this.reportLines.filteredData.length > 0){
      const items: CsvData[] = [];

      this.reportLines.filteredData.forEach(line => {
        let reportDate = new Date(report.date);
        let csvLine: CsvData = {
          date: `${reportDate.getDate()}/${reportDate.getMonth()+1}/${reportDate.getFullYear()}`,
          laborerName: line.laborerName,
          machineNumber: line.machineNumber,
          machineName: line.machineName,
          workingHours: line.hours,
          description: line.description
        }
        items.push(csvLine); 
      });

      this.csvService.exportToCsv('myCsvDocumentName.csv', items);
    }
    
  }

3 Comments

this solutions adds extra quotation marks to the beginning and end of strings
Note that as of 2022, msSaveBlob is no longer available as part of the navigator: stackoverflow.com/questions/69485778/…
@glenatron I've updated the answer to include the global declaration as provided by your answer
0

The 'file-saver' library has been causing some memory overflow issues in my project. To solve the download, without the 'file-saver', I am using the function:

  download(filename: string, data: any) {
    let link = document.createElement('a');
    link.download = filename;
    let blob = new Blob(data, { type: 'text/csv' });
    link.href = URL.createObjectURL(blob);
    link.click();
    URL.revokeObjectURL(link.href);
  }

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.