Whats the best strategy for letting users download ember-data returns as CSVs?

I’m still new to Ember and application development in general and have run into a bit of a break wall. I have a table in my application that uses html/handlebars to print data from a model. I have a requirement that should allow a user to download the table into a CSV file.

I initially tried this with DatatablesJS but that broke the two-way binding between my model and the query params I am using to generate the model which I can’t allow.

I’m not sure what the best strategy is now to allow the user to export this information. I did find this add on but I have been wholly unable to get it to produce a file using the ‘Model’ method in its documentation. I get a bit lost at the end and I am unsure what to do with the

invoiceModel
  .download()
  .then((pdfContent) => this.saveFileAs('invoice.pdf', pdfContent, 'application/pdf'));

I have also seen suggestions that the request for a CSV file should be sent to the backend and have it return through a CSV generator but it seemed very confusing.

I can’t imagine this is a unique requirement, how do other people approach it?

I did this recently. I created a service called “download”:

import Service from '@ember/service';

export default Service.extend({
  asCSV(filename, contents) {
    let { document, URL } = window;
    let anchor = document.createElement('a');
    anchor.download = filename;
    anchor.href = URL.createObjectURL(new Blob([contents], {
      type: 'text/csv'
    }));

    document.body.appendChild(anchor);
    anchor.click();
    anchor.remove();
  }
});

Somewhere in my app, I did this.get('download').asCSV('somefile.csv', csv); where csv is the data that I formatted on the client side.

3 Likes

Thats a cool idea. Still being new to Ember services are something I often forget are available to me.

In terms of the data formatting, are you able to just plant a model in there or do you create a specific dataset in the controller? Would you mind posting an example dataset?

I put a method on my report model called toCSV() that created a CSV string from its attributes. You could that logic in the controller too, or another service. The string should just be something like:

id,name
1,David
2,Sam

Thanks for your help! I’ve managed to create a component button that will produce and download a CSV file thanks to the above information.

I struggled with parsing an object into a CSV string but found Papa parse which is doing a bang up job.

My last hurdle is working out how to do this in a model method like you mentioned. The only way I can get it to work at the minute is by explicitly iterating on the model in the component controller which means it isn’t very flexible:

export default Component.extend({
  download: service('download'),

  model: null,
  conData: computed('model', function() {
    let newArray = [];
    this.get('model').forEach(function(x) {
      let newData = {
        name: x.data.name,
       age: x.data.age
      }
      newArray.push(newData)
    })
    return newArray
  }),

  actions: {
    request() {
      let csvArray = Papa.unparse(this.get('conData'))
      this.get('download').asCSV('GeneratedReport.csv', csvArray)
    }
  }
});

The only thing I’vve done with models so far is to populate them from a JSON return.