parsing CSV in Javascript and AngularJS

All we need is an easy explanation of the problem, so here it is.

So I’m trying to create a basic angular application that parses some CSV input, and fills a table with the parsed data.

You can see a plunker of what I’m trying to achieve here – http://plnkr.co/edit/6QFT4AcV4KpiSG23EdOS

Basically – as you can see – I have a <textarea> where the user will paste in some CSV, and the table below should then be filled with the data.

<div class="excelArea">
    <textarea name="excelData" ng-model="excelData"></textarea>
</div>

This is the javascript I have so far, but I’m struggling with a few things
1. Seperating the email from the name
2. Pushing the data back into the $scope.inviteList;

app.controller("InviteController", function($scope) {

    //Initliase objects
    $scope.excelData = "";
    $scope.errorMessage = "";
    $scope.inviteList = [];

    $scope.$watch("excelData", function() {

        var lines, lineNumber, data, length;

        lines = $scope.excelData.match(/[^\r\n]+/g);
        lineNumber = 0;

        for (var i = lines.length - 1; i >= 0; i--) {

            l = lines[i];
            lineNumber++;
            data = l.split(/\t/);

            var email = ? ? ?
            var name = ? ? ?

            $scope.inviteList.push({
                name: name,
                email: email,
                status: "not sent"
            });

        };

    });

});

Some basic information:

The CSV will be two columns (name, email) and will look like this:

John Thompson,[email protected]
Robin Peters, [email protected]
Bill Bob, [email protected]

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Many problems in your code :

  • You could juste use split on your input instead of regexes, it makes everything easier
  • Your HTML isn’t valid td should be inside tr and not the other way around
  • Your array was never cleared
  • Your bindings inside ng-repeat didn’t use variable i defined here : i in inviteList
  • You should avoid unscoped variables (without var keyword) whenever possible

Otherwise, when you split a string, just access the splitted elements through their index.

Corrected code :

JS

$scope.$watch("excelData", function() {
    var lines, lineNumber, data, length;
    $scope.inviteList = [];
    lines = $scope.excelData.split('\n');
    lineNumber = 0;
    for (var i = lines.length - 1; i >= 0; i--) {
        l = lines[i];

        lineNumber++;
        data = l.split(',');

        var name = data[0];
        var email = data[1];

        $scope.inviteList.push({
            name: name,
            email: email,
            status: "not sent"
        });
    }
});

HTML

  <table>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Status</th>
    </tr>
    <tr ng-repeat="i in inviteList">
      <td>{{i.name}}</td>
      <td>{{i.email}}</td>
      <td>{{i.status}}</td>
    </tr>
  </table>

Your code (especially JS) can still be improved a lot and i encourage you to read docs/tutorials more.

And here is the plunker to your working code.

Method 2

I would create a filter to parse the data and reuse it anywhere I want it to.
Logic:

app.controller('MainCtrl', function($scope, $filter) {
  $scope.data = 'John Thompson,[email protected]\nRobin Peters, [email protected]\nBill Bob, [email protected]';

  $scope.$watch('data', function(val){
    $scope.inviteList = $filter('csvToObj')(val);
  });
});

app.filter('csvToObj', function() {
  return function(input) {
    var rows = input.split('\n');
    var obj = [];
    angular.forEach(rows, function(val) {
      var o = val.split(',');
      obj.push({
        name: o[0],
        email: o[1],
        status: "not sent"
      });
    });
    return obj;
  };
});

Sample Demo:http://plnkr.co/edit/SOtMMLft3amlVm4RROd0?p=preview

Method 3

When you split a string, the return value is an array.
I believe that you only have to access the correct indexes of the data array:

app.controller("InviteController", function($scope) {

    //Initliase objects
    $scope.excelData = "";
    $scope.errorMessage = "";
    $scope.inviteList = [];

    $scope.$watch("excelData", function() {

        var lines, lineNumber, data, length;

        lines = $scope.excelData.match(/[^\r\n]+/g);
        lineNumber = 0;

        for (var i = lines.length - 1; i >= 0; i--) {

            l = lines[i];
            lineNumber++;
            data = l.split(/\t/);

            var email = data[1];
            var name = data[0];

            $scope.inviteList.push({
                name: name,
                email: email,
                status: "not sent"
            });

        };

    });

});

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply