r3plica r3plica - 5 months ago 181
AngularJS Question

Angular CSV reader post results to controller

I have created a CSV reader using https://github.com/bahaaldine/angular-csv-import as a reference.
It currently looks like this:

.directive('csvReader', [function () {

// Function to convert to JSON
var convertToJSON = function (content) {

// Declare our variables
var lines = content.csv.split('\n'),
result = [],
headers = lines[0].split(content.separator),
columnCount = lines[0].split(content.separator).length;

// For each row
for (var i = 1; i < lines.length; i++) {

// Declare an object
var obj = {};

// Get our current line
var line = lines[i].split(new RegExp(content.separator + '(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)'));

// For each header
for (var j = 0; j < headers.length; j++) {

// Populate our object
obj[headers[j]] = line[j];
}

// Push our object to our result array
result.push(obj);
}

// Return our array
return result;
};

return {
restrict: 'A',
scope: {
result: '=',
separator: '=',
callback: '&saveResultsCallback'
},
link: function (scope, element, attrs) {

// Create our data model
var data = {
csv: null,
separator: scope.separator || ','
};

// When the file input changes
element.on('change', function (e) {

// Get our files
var files = e.target.files;

// If we have some files
if (files && files.length) {

// Create our fileReader and get our file
var reader = new FileReader();
var file = (e.srcElement || e.target).files[0];

// Once the fileReader has loaded
reader.onload = function (e) {

// Get the contents of the reader
var contents = e.target.result;

// Set our contents to our data model
data.csv = contents;

// Apply to the scope
scope.$apply(function () {

// Our data after it has been converted to JSON
scope.result = convertToJSON(data);

// Call our callback function
scope.callback(scope.result);
});
};

// Read our file contents
reader.readAsText(file);
}
});
}
};
}])


My HTML looks like this:

<div class="form-group">
<label class="control-label">CSV file</label>
<input type="file" csv-reader result="csv.results" save-results-callback="controller.saveResults(something)" />
</div>

<div class="block right" ng-if="controller.results">
<h2>JSON</h2>
<div class="content">{{ csv.results }}</div>
</div>


When I pick my CSV file, the contents are displayed on my view correctly. Now I wish to take them contents and save them to my database, but I can not get it to work properly.
I was hoping that if I changed my csv.results to controller.results I would be able to access the results in my controller (I am using controllerAs and specifying "controller" as the name) but I just get undefined.

You can see I have tried adding a callback function but this doesn't work either.

Does anyone know how to solve my problem?

Answer

This was actually easier than I thought. Basically in my controller I specify a holder variable (self.results = []) and then I pass that to the directive. The directive then populates that with the formatted JSON and 2 way binding handles the rest.

Incase anyone else needs to know, here is what I did. I set up my controller like this:

.controller('ImportCollectionsController', ['$stateParams', 'UploadService', function ($stateParams, service) {

    // Assign this to a variable
    var self = this;

    // Used to validate the imported data
    self.validateResults = function (results) {

        // Validate our results
        var errors = service.validateImport(results);

        // If there are no errors
        if (errors.length === 0) {

            // Set our results object to our results
            self.results = results;
        } else {

            // Otherwise, display our errors
            self.errors = errors;
        }
    };

    // Save our data
    self.save = function () {

        // Log our results to make sure we actually have some
        console.log(self.results);

        // --- omitted for brevity --- //
    };
}]);

And my HTML looks like this:

<div class="form-group" ng-hide="controller.results">
    <label class="control-label">CSV file</label>
    <input type="file" csv-reader complete="controller.validateResults(results)" />
</div>

<div class="block right" ng-if="controller.results.length">
    <h2>JSON</h2>

    <div class="content">{{ controller.results }}</div>

    <div class="form-group">
        <button class="btn btn-primary" ng-click="controller.save()">Import</button>
    </div>
</div>

When the button is clicked, it calls controller.save() which in turn displays the array of json objects. Here is my directive just to complete this answer:

.directive('csvReader', [function () {

    // Function to convert to JSON
    var convertToJSON = function (content) {

        // Declare our variables
        var lines = content.csv.replace(/[\r]/g, '').split('\n'),
            headers = lines[0].split(content.separator),
            results = [];

        // For each row
        for (var i = 1; i < lines.length - 1; i++) {

            // Declare an object
            var obj = {};

            // Get our current line
            var line = lines[i].split(new RegExp(content.separator + '(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)'));

            // For each header
            for (var j = 0; j < headers.length; j++) {

                // Populate our object
                obj[headers[j]] = line[j];
            }

            // Push our object to our result array
            results.push(obj);
        }

        // Return our array
        return results;
    };

    return {
        restrict: 'A',
        scope: {
            results: '=',
            separator: '=',
            complete: '&'
        },
        link: function (scope, element) {

            // Create our data model
            var data = {
                csv: null,
                separator: scope.separator || ','
            };

            // When the file input changes
            element.on('change', function (e) {

                // Get our files
                var files = e.target.files;

                // If we have some files
                if (files && files.length) {

                    // Create our fileReader and get our file
                    var reader = new FileReader();
                    var file = (e.srcElement || e.target).files[0];

                    // Once the fileReader has loaded
                    reader.onload = function (e) {

                        // Get the contents of the reader
                        var contents = e.target.result;

                        // Set our contents to our data model
                        data.csv = contents;

                        // Apply to the scope
                        scope.$apply(function () {

                            // Our data after it has been converted to JSON
                            var results = convertToJSON(data);

                            // If we have a callback function
                            if (scope.complete) {

                                // Execute our callback
                                scope.complete({ results: results });
                            }
                        });
                    };

                    // Read our file contents
                    reader.readAsText(file);
                }
            });
        }
    };
}])

I hope this helps someone else.

Comments