Ben
Ben

Reputation: 16649

ngProgress loading bar on every page load

I am using AngularJS and ngProgress to display a YouTube-like loading bar at the top of my site.

The bar is started, then new data is loaded in via ajax, and once the request is finished, the bar is completed.

Example:

var TestCtrl = function( $scope, $location, Tests, ngProgress ) 
{
    // start progressbar
    ngProgress.start();

    $scope.tests = Tests.query(
        function()
        {
            // end progressbar
            ngProgress.complete()
        }
    );
};  

Now my question is: How can I integrate this principle higher up in the order of things, such that I don't have to repeat the code for every single controller?

Upvotes: 3

Views: 5207

Answers (4)

Zeecher Game
Zeecher Game

Reputation: 169

Here is an example using Interceptor:

.factory('interceptorNgProgress', [
    'ngProgressFactory', function (ngProgressFactory) {

var complete_progress, getNgProgress, ng_progress, working;
ng_progress = null;
working = false;

getNgProgress = function() {

    if(!ng_progress) {

        ng_progress = ngProgressFactory.createInstance();

        return ng_progress;
    }

    return ng_progress;
};

complete_progress = function() {
    var ngProgress;
    if (working) {
        ngProgress = getNgProgress();
        ngProgress.complete();
        return working = false;
    }
};

return {
    request: function(request) {
        var ngProgress;
        ngProgress = getNgProgress();
        if (request.url.indexOf('.html') > 0) {
            return request;
        }
        if (!working) {
            ngProgress.reset();
            ngProgress.start();
            working = true;
        }
        return request;
    },
    requestError: function(request) {
        complete_progress();
        return request;
    },
    response: function(response) {
        complete_progress();
        return response;
    },
    responseError: function(response) {
        complete_progress();
        return response;
    }
}
}])

.config(function ($httpProvider) {
    $httpProvider.interceptors.push('interceptorNgProgress');
});

Upvotes: 1

Mauricio Poppe
Mauricio Poppe

Reputation: 4876

You could use a service which controls ngProgress (acting like a wrapper over it) and listen for changes in the url.

  • Each time the url changes the event $locationChangeSuccess is broadcasted (more info at $location) which we could listen to invoke ngProgress.start()
  • However we don't know when it's completed (we can't have a bar on the top loading forever), therefore we need to call ngProgress.complete() explicitly in our controllers OR we could assume that our async functions might take like 5 seconds to be completed and call ngProgress.complete() using a timer in our wrapper service
  • When the loading bar is already visible and there's a change in the url we need to reset the status of the bar by calling ngProgress.reset()

You can use the following approach to solve these problems:

angular.module('myApp').factory('Progress', function (ngProgress) {
    var timer;
    return {
        start: function () {
            var me = this;
            // reset the status of the progress bar
            me.reset();
            // if the `complete` method is not called
            // complete the progress of the bar after 5 seconds
            timer = setTimeout(function () {
                me.complete();
            }, 5000);
        },
        complete: function () {
            ngProgress.complete();
            if (timer) {
                // remove the 5 second timer
                clearTimeout(timer);
                timer = null;
            }
        },
        reset: function () {             
            if (timer) {
                // remove the 5 second timer
                clearTimeout(timer);
                // reset the progress bar
                ngProgress.reset();
            }
            // start the progress bar
            ngProgress.start();
        }
    };
});

To listen for changes in the url and show the progress bar we could use:

angular.module('myApp')
    .run(function (Progress) {
        $rootScope.$on('$locationChangeSuccess', function () {
            Progress.start();
        });
    }

Now we can manually control the completeness of the status bar by injecting the Progress service and calling the method Progress.complete() when all of our async functions have finished (we could also control this from any service that makes async calls):

angular.module('myApp')
    .controller('SomeCtrl', function (Progress) {
        setTimeout(function () {
            Progress.complete();
        }, 2000);
    });

Upvotes: 2

Sarah Vessels
Sarah Vessels

Reputation: 31640

You apparently already have a Tests service. Override it so ngProgress is injected into it and have Tests always call ngProgress.start() and ngProgress.complete() for you in query.

Upvotes: 0

Richard V
Richard V

Reputation: 73

I would put it in an angular directive and then you can pass that into any controller you want to be able to use it.

http://docs.angularjs.org/guide/directive

Edit, thinking about it a service mght be better for this case.

http://docs.angularjs.org/guide/dev_guide.services.creating_services

Upvotes: 0

Related Questions