When you click the submit button, an array of files ( $scope.files can be just one file, as much as the user wants) gets through FormData and XMLHttpRequest in my function angular $scope.uploadFiles :
$scope.uploadFiles = function() { for (var i in $scope.files) { var form = new FormData(); var xhr = new XMLHttpRequest; // Additional POST variables required by the API script form.append('destination', 'workspace://SpacesStore/' + $scope.currentFolderUUID); form.append('contenttype', 'idocs:document'); form.append('filename', $scope.files[i].name); form.append('filedata', $scope.files[i]); form.append('overwrite', false); xhr.upload.onprogress = function(e) { // Event listener for when the file is uploading $scope.$apply(function() { var percentCompleted; if (e.lengthComputable) { percentCompleted = Math.round(e.loaded / e.total * 100); if (percentCompleted < 1) { // .uploadStatus will get rendered for the user via the template $scope.files[i].uploadStatus = 'Uploading...'; } else if (percentCompleted == 100) { $scope.files[i].uploadStatus = 'Saving...'; } else { $scope.files[i].uploadStatus = percentCompleted + '%'; } } }); }; xhr.upload.onload = function(e) { // Event listener for when the file completed uploading $scope.$apply(function() { $scope.files[i].uploadStatus = 'Uploaded!' setTimeout(function() { $scope.$apply(function() { $scope.files[i].uploadStatus = ''; }); }, 4000); }); }; xhr.open('POST', '/path/to/upload/script'); xhr.send(form); } }
The problem is that var i increases the initial cycle of the cycle for each file, and by the time the fire listens to the event, i has already increased by the desired value of files[i] , but only the last one in the array. I use .uploadStatus as a means of interactively displaying the progress of each individual file for the user, so for each array element in $scope.files I will need a separate event listener. How to assign and track events for individual elements of an array in Angular?
UPDATE
I reworked two event listeners with little success, but I still experience odd behavior:
xhr.upload.onprogress = (function(file) { // Event listener for while the file is uploading return function(e) { $scope.$apply(function() { var percentCompleted = Math.round(e.loaded / e.total * 100); if (percentCompleted < 1) { file.uploadStatus = 'Uploading...'; } else if (percentCompleted == 100) { file.uploadStatus = 'Saving...'; } else { file.uploadStatus = percentCompleted + '%'; } }); } })($scope.files[i]); xhr.upload.onload = (function(file, index) { // Event listener for when the file completed uploading return function(e) { $scope.$apply(function() { file.uploadStatus = 'Uploaded!' setTimeout(function() { $scope.$apply(function() { $scope.files.splice(index,1); }); }, 2000); }); } })($scope.files[i], i);
.onprogress seems to go away without a hitch, but with a few changes made to .onload , now I see a lot of strange behavior with two-way AngularJS binding for my templates. for each element in the $scope.files array, a status is set using the aforementioned .uploadStatus property. Now I have setTimeout splice elements from the array using the i variable, which is passed to the self-executing function. Oddly enough, downloads overlap simultaneously on about 6 simultaneous downloads, which should be a problem on the server side, but I notice that as the array elements receive splicing, the ng-repeat in the template acts strangely, where will it splicer the element, not necessarily the ones he needs. I also noticed that records often appear that don't get spliced ββafter a threshold of 2000 milliseconds is reached.
Does this resemble the original problem when the variable i unreliable when referenced during the start of event listeners? Now I pass it to the anonymous self- .onload function .onload , and splicing uses it to determine which element of the array should delete, but it does not necessarily delete the correct one and often leaves other elements in the array when it needs to delete them.