Search code examples
javascriptangularjsjs-xlsx

Upload excel files and consume data


I'm trying to create a moule where the user can upload an excel file an get data from the document. I'm using the js-xlsx library. Right now, with the next code, i'm getting the info on the console of my example file as a json:

$scope.ExcelExport= function (event) {

    var input = event.target;
    var reader = new FileReader();
    reader.onload = function(){
        var fileData = reader.result;
        var wb = XLSX.read(fileData, {type : 'binary'});

        wb.SheetNames.forEach(function(sheetName){
        var rowObj = XLSX.utils.sheet_to_json(wb.Sheets[sheetName]);
        $scope.jsonObj = rowObj;
        console.log($scope.jsonObj);
        })

    };
    reader.readAsBinaryString(input.files[0]);
    };

I know i have to save the document, but: there's a way to stored the readed info on my console and show it on the html view?

Per example, let's say that my example file have the next data in two columns:

Person | Job |(This is the header) Chuck | Developer| John | Teacher |

And i want to populate a table:

<div class="row">
<div class="col-lg-11">
    <form class="form-inline">
        <div class="am form-group">
        </div>
        <div class="container">
            <table class="table table-hover">
                <thead>
                <tr>
                    <th>Person</th>
                    <th>Job</th>
                </tr>
                </thead>
                <tbody>
                <tr ng-repeat="x in jsonObj">
                    <th>{{x.Person}}</th>
                    <th>{{x.Job}}</th>
                </tr>
                </tbody>
            </table>
        </div>
    </form>
</div>

I'm using angularjs and Javascript.

Thanx in advance!


Solution

  • As charlietfl correctly pointed out, you have to call $scope.$apply() whenever you change something outside of angular.

    As for the error TypeError: Cannot read property 'charCodeAt' of null, change:

    var fileData = reader.result;

    to

    var fileData = input.result;

    Here's how I'd organize this feature.

    Your directive:

    angular.module("app").directive("importSheetJs", function($parse) {
      return {
        link: function($scope, $elm, $attrs) {
          // Parse callback function from attribute
          var expressionHandler = $parse($attrs.onSheetLoad);
    
          // Pass upload event to callback    
          $elm.on("change", function(changeEvent) {
            var reader = new FileReader();
            reader.onload = function(e) {
              expressionHandler($scope, { e: e });
            };
            reader.readAsBinaryString(changeEvent.target.files[0]);
          });
        }
      };
    });
    

    Your controller:

    angular.module("app").controller("MainController", function($scope) {
      $scope.loadWorksheet = function(e) {
        // Read Workbook
        var file = e.target.result;
        var workbook = XLSX.read(file, { type: "binary" });
    
        // Get the first worksheet as JSON
        var sheetName = workbook.SheetNames[0];
        $scope.sheet = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]);
    
        // Log it and update scope
        console.log(sheet);
        $scope.sheet = sheet;
        $scope.$apply(); // Need this to update angular with the changes
      };
    });
    

    Then in your html:

    <input type="file" import-sheet-js="" on-sheet-load="loadWorksheet(e)" multiple="false" />  
    

    Here's a working example on codepen