Search code examples
angularjsangular-file-upload

Why is my AngularJS binding not always updating properly?


I'm creating a simple image uploading page that works with AWS S3 using Danial Farid's ng-file-upload plugin. It features a simple button to upload a new image and a list of images. Like this:

<ul>
    <li ng-repeat="image in product.images">
        <a class="thumbnail" href="{{ image }}" target="_blank">
          <img ng-src="{{ image }}" alt="image">
        </a>
    </li>
    <li>
        <div class="btn-image-upload" 
            ngf-select 
            ngf-change="upload($files)"
            ngf-accept="'image/*'"></div>
    </li>
</ul>

On my controller:

$scope.upload = function (files) {

    var aws_policy, aws_policy_signature;

    if (files && files.length) {
        $http
            .get(API_URL+'/helper/aws_policy')
            .then(function(data) {
                aws_policy = data.data.policy;
                aws_policy_signature = data.data.signature;
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    var key = generate_aws_key() + '.' + file.name.split('.').pop();
                    var base_url = 'https://.../';

                    Upload
                    .upload({ ... })
                    .success(function( data, status, headers, config ) {

                        console.log($scope.product);
                        $scope.product.images.push( base_url + key );
                        console.log($scope.product);

                    });
                }
            });
    }
};

The file is being uploaded properly (I get a nice 204 Success from AWS) and the two console.log($scope.product)'s get called showing the appropriate results (the second one features the item on the images array).

Here's the thing. This baby works perfectly on my development machine, but sometimes on the staging or production servers, the list of images is not being updated when $scope.product.images is. By sometimes, I mean that it does it 1/3rd of the times.

Summarizing

Sometimes, only on my production server, the DOM is not updated when $scope.product.images is updated inside AngularJS digest.

My programming experience taught me that sometimes is not an acceptable concept in this type of matters and that it must be related to something that always happens when this problem arises, but I debugged extensively and failed to find the real cause. Ideas?


Solution

  • It's very likely caused by looping within asynchronous callbacks:

    You have to freeze the value of i:

    $scope.upload = function (files) {
    
        var aws_policy, aws_policy_signature;
    
        function upload(index) {
            var file = files[index];
            var key = generate_aws_key() + '.' + file.name.split('.').pop();
            var base_url = 'https://.../';
    
            Upload
            .upload({ ... })
            .success(function( data, status, headers, config ) {
    
                console.log($scope.product);
                $scope.product.images.push( base_url + key );
                console.log($scope.product);
    
            });
        }
    
        if (files && files.length) {
            $http
                .get(API_URL+'/helper/aws_policy')
                .then(function(data) {
                    aws_policy = data.data.policy;
                    aws_policy_signature = data.data.signature;
                    for (var i = 0; i < files.length; i++) {
                        var file = files[i];
                        upload(i);
                    }
                });
        }
    };