37

I need to upload file and I use $http (this code is get from my .service() function):

sendFile: function (params) {
            return $http({method : 'post',
                url : 'http://XXXXXXXXXXXXX/rest/file.json',
                headers : { 'X-CSRF-Token' : $cookies['csrftoken']},
                data : params
            })
        },

Now, for little file and a good line there is no problem, but with a big file and/or a bad/slow line there is a problem of UI: user can not know when upload will be end. I need a progress bar.

So I have search on internet, but I have not found a solution. Is there a possibility to get some progress/notification from $http ?

I have tried this code without luck:

ProfileService.sendFile(data)
                    .then(function(ret) {
                            var uri = ret.data.uri;
                            scope.content = "Upload finished";

                            scope.postForm.fid = ret.data.fid;
                            scope.postForm.buttonDisabled = false;
                        },
                        function(error) {
                            scope.postForm.showError = true;
                            scope.postForm.errorMsg = error.data;
                        },
                        function(progress) {
                            console.log("inside progress");
                            console.log(progress)
                        }
                    );

“progress” function is never called.

I'm using angular 1.2.x

Thanks.

Muhammad Reda
  • 26,379
  • 14
  • 93
  • 105
ZioBudda
  • 948
  • 2
  • 12
  • 24
  • you can probably use you buttonDisabled boolean to show/hide a loading gif and that would take care of your problem – ruedamanuel Apr 21 '14 at 21:34
  • 3
    Thanks, but "loading" is not "progress". I want to show to my user where the upload is. – ZioBudda Apr 22 '14 at 02:04
  • Did you find any solution for this? I have kind of the same problem. I am trying to get upload progress info from http request but I have not found any. All of the answers below are talking about the UI aspect but not the actual work behind it. – Besat Aug 04 '16 at 12:54

5 Answers5

51

You can use Angular Loading Bar. It works automatically for $http requests and does not need any setup except adding it as app dependency.

angular.module('app', ['angular-loading-bar']); // that's all
Muhammad Reda
  • 26,379
  • 14
  • 93
  • 105
  • 1
    I just added **angular-loading-bar** and it's awesome :D Tiny, easy to integrate and fully customisable :) – Primoz990 Feb 10 '15 at 14:09
  • 12
    This does not actually track the file upload at all, interesting that a lot of people upvoted !!!! The code at line 255 describes it /** * Increments the loading bar by a random amount * but slows down as it progresses */ – appbootup Jul 06 '16 at 09:06
  • This is the only plugin that is actually helpful rest answers are just experimental on this topic. – sanky Feb 04 '17 at 08:48
  • It seemed to reported an incorrect percentage when I uploaded a file (as a base64 string) – Anders Lindén Dec 05 '17 at 10:18
6

I would highly recommend ng-progress. This handles multiple requests and basically shows a neat little progress bar for all http activity.

http://victorbjelkholm.github.io/ngProgress/

mithun_daa
  • 4,334
  • 5
  • 38
  • 50
  • I need to get "progress" from $http before I can use ngProgress. So, how can I get "progress" from $http ? – ZioBudda Apr 22 '14 at 07:56
  • @mithun_daa No, ngProgress doesn;t implement any $http interceptor. As far as I know, the $q promise api doesn't give any information about the download progress of a request. This thread gives some insights : https://github.com/angular/angular.js/issues/1934 – Ant Aug 08 '14 at 01:36
  • 1
    @Cooluhuru My bad, I was thinking nProgress not ngProgress. nProgress does hook into the interceptors. – mithun_daa Aug 11 '14 at 16:00
  • http://chieffancypants.github.io/angular-loading-bar/# -- anotehr option that uses inteceptors. – MCGRAW Oct 16 '14 at 15:07
3

You can solve this by hiding/showing a loadingbar. Start showing the loadingbar once the upload starts, and remove the loadingbar when the upload is done (in the success handler from the $http request).

I created a simple jsfiddle for you to show you an example.
I'm using $timeout to simulate a $http request.

Html markup:

<div ng-controller="MyCtrl">
    <!-- this can be an image if you want -->
    <p ng-show="loading">...LOADING...</p>

    <!-- Showing the status -->
    <p ng-hide="loading">{{status}}</p>
    <button type="button" ng-click="upload()">Do $http request</button>
</div>

Js Controller:

function MyCtrl($scope, $timeout) {
    $scope.status = 'Not started';
    $scope.loading = false;

    $scope.upload = function() {
        $scope.loading = true;
        // Simulating a http request with a timeout
        $timeout(function(){ 
            $scope.status = "Finished";
            $scope.loading = false;
        },3000);
    }
}

For a demonstration of how this works, see this fiddle.

Update

By clarification in the comments, you want to be able to track the progress on the upload by percentage. eg. How many % til the upload is finished

You should check out this SO post where this has already been discussed.

From the accepted answer:

I don't think $http.post() can be used for this. As for client-side, it should work with an HTML5 browser, but you'll probably have to create your own XMLHttpRequest object and onprogress listener. See AngularJS: tracking status of each file being uploaded simultaneously for ideas.

Community
  • 1
  • 1
aludvigsen
  • 5,893
  • 3
  • 26
  • 37
  • Hi, thanks but I want a progress bar, not a "Loading" in a Div. Thanks however. – ZioBudda Apr 22 '14 at 02:04
  • Do you want to show a [progress bar like this](http://jsfiddle.net/GA9t8/3/)? Or do you want to display the actual progress, % bytes/total sent to the server? @ZioBudda – aludvigsen Apr 22 '14 at 05:33
  • The 2nd: I need to display %bytes/total (in a progress bar). Thanks. – ZioBudda Apr 22 '14 at 07:54
2

You can simply use the eventHandlers of the $http service

like:

mainModule.service('File', function (Api) {

    var controller = 'files';

    function File(data) {
        this.$data = data;
    }

    File.__proto__ = File.prototype = {
        upload: function (progress) {
            var fd = new FormData();
            fd.append('file', this.$data);
            return pack('/upload').post(fd, {
                transformRequest: angular.identity,
                uploadEventHandlers: {'progress': progress},
                headers: {'Content-Type': undefined}
            });
        }
    };

    return File;
    function pack(action) {
        return Api(controller + action);
    }
});

Api is a service to connect with the server api.

$data is the file object from the input

1

If you don't want to write show hide in every single http method, then we can create a simple directive by using $http.pendingRequests.length and and that's it.

Whenever we will have any http request in progress it will be showing automatically.

app.directive('loading', ['$http', function ($http) {
    return {
        restrict: 'A',
        link: function (scope, elm, attrs) {
            scope.isLoading = function () {
                return $http.pendingRequests.length > 0;
            };
            scope.$watch(scope.isLoading, function (v) {
                if (v) {
                    elm.show();
                } else {
                    elm.hide();
                }
            });
        }
    };
}]);

AND HTML

 <div data-loading>
   Please wait...
 </div>

For more detail see this

Ali Adravi
  • 21,707
  • 9
  • 87
  • 85