Search code examples
javascriptajaxangularjsqangular-file-upload

Angular JS - $q.all() tracking individual upload progress


I'm using angular-file-upload module by danialfarid (https://github.com/danialfarid/angular-file-upload) and it works great.

I've been able to integrate in my wrapper service for REST calls and I can actually upload several images with $q.all() and keeping track of their progress.

However, I can't correctly identify the single images I'm uploading, because the file identifier gets continuosly changed by the for loop.

      uploadPhotos: function (files) {

        var deferred = $q.defer()
        var queue = []

        for (var i = 0; i < files.length; i++) {
          var file = files[i];
          var up = $upload.upload({
            url: locationURI +'/photos',
            file: file,
            fileFormDataName: 'image'
          }).then(
            function (data) {
              console.log(data)
            },
            function (err) {
              console.log(err)
            },
            function(evt) {
              // progress events
              console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
            }
          )
          queue.push(up)
        }

        $q.all(queue).then(
          function (data) {
            deferred.resolve(data)
          },
          function (err) {
            deferred.reject(err)
          }
        )

        return deferred.promise
      }

This is, without any surprise, the confused output I get:

    percent: 68 restfactory.js:359
    percent: 100 restfactory.js:359
    percent: 100 restfactory.js:359
    percent: 14 restfactory.js:359
    percent: 37 restfactory.js:359
    percent: 52 restfactory.js:359
    percent: 89 restfactory.js:359
    percent: 100 restfactory.js:359
    percent: 100 restfactory.js:359

Do you have any idea how could I manage to have something like:

    file1 - percent: 68 restfactory.js:359
    file1 - percent: 100 restfactory.js:359
    file2 - percent: 100 restfactory.js:359

Solution

  • Closures within loops are tricky. Closing on the loop variable will always get the last value (e.g. see this question - and google it for more theory/details). What you want would be to call another function within the loop:

    for (var i = 0; i < files.length; i++) {
        var up = doTheUpload(files[i]);
        queue.push(up);
    }
    

    And doTheUpload() contains your original code, returning the promise and using the correct file (I do not know the API, I presume file.name is contains the file name; adjust appropriately):

    function doTheUpload(file) {
          var up = $upload.upload({
            url: locationURI +'/photos',
            file: file,
            fileFormDataName: 'image'
          }).then(
            function (data) {
              console.log(data)
            },
            function (err) {
              console.log(err)
            },
            function(evt) {
              // progress events
              console.log(file.name + ' percent: ' + parseInt(100.0 * evt.loaded / evt.total));
            }
          );
          return up;
    }