I am creating a directive that helps with downloading a CSV file. For this, I use <input type="file" ng-hide="true"/> and my own JavaScript FileReader() . Everything works fine when I select a file using native input , but I tried to customize the file control using my own Browse button. The problem is that when I try to do this, I get that the value of $ apply is already running.
Here is my directive
app.directive('skUploader', function ($timeout) { return { restrict: 'A', replace: true, template: '<div>\ <input type="file" class="uploadControl" ng-hide="false" />\ <input type="text" ng-model="selectedFile" />\ <button type="button" class="btnPlain" ng-click="triggerBrowse()">Browse</button>\ <button type="button" class="btnOrange fRight" ng-click="uploadCSV()">Upload</button>\ <button type="button" class="btnPlain fRight" ng-click="cancel()">Cancel</button>\ </div>', link: function (scope, elem, attr, ctrl) { var uploadControl = elem.find('input[type=file]'); var payload = null; var type = attr.skUploader; scope.selectedFile = null; scope.uploadCSV = function () { $timeout(function () { alert('Upload Successful'); }, 3000); } scope.cancel = function () { payload = null; scope.selectedFile = null; scope.model.showUploadPanel = false; } uploadControl.on('change',function () { var fileHandle = uploadControl[0].files[0]; var reader = new FileReader(); var buffer; scope.selectedFile = fileHandle.name; reader.onload = function(){ buffer = reader.result; payload = parseCSV(buffer); if (payload) { // Make API call if (type == 'mapping') { console.log(payload); } else { } } } if (fileHandle.name.indexOf('.csv') > 0) { reader.readAsText(fileHandle); } else { } }); function parseCSV(buff){ var entries = []; entries = buff.split(/\n/); var json = []; if (type == 'mapping') { var formatter = { iRowNumber: null, SourceName: null, DestName: null }; } else if (type == 'passwords') { var formatter = { iRowNumber: null, SourceCreds: { EmailAddress: null, Username: null, Pwd: null } }; } else { return null; } for(var i = 1; i < entries.length - 1; i++){ var split = entries[i].split(','); var obj = angular.copy(formatter); var j = 0; angular.forEach(formatter, function (value, key) { if (key == 'iRowNumber') { obj[key] = i; } else if (key == 'SourceCreds') { obj[key] = { EmailAddress: split[0], Username: split[1], Pwd: split[2] }; }else{ obj[key] = split[j]; j++; } }); json.push(obj); } return json; } scope.triggerBrowse = function () { uploadControl.trigger('click'); // THIS IS WHAT CAUSES THE ERROR } } }; });
When I use the browse button on trigger('click') , uploadControl, Angular spits out an error
Error: [$rootScope:inprog] $apply already in progress
But I do not use scope.$apply() anywhere, so I can not understand the problem. It should just fire the click event on <input type="file"/> , which should trigger its own OS file explorer.
Update
Maybe this is due to this problem ?
Update 2
After some more research, I found this Git problem in which vojta (the core of AngularJS dev) replied that we needed to wrap the event in $timeout , I did it and it really worked! Can someone explain to me why this is necessary in detail? Thanks you
angularjs
Matt hintzke
source share