How to do preloader for `ng-view` in Angular JS?

Hamed picture Hamed · May 7, 2015 · Viewed 12.5k times · Source

I use <div ng-view></div> on web page. When I click link in block <div> is loaded HTML template was set in routeProvider. Also together is done request AJAX that returns data that was loaded template.

Now problem is that after click I get HTML template with empty form, still is working AJAX request. After some seconds form HTML is fiiled data from AJAX.

How I can do preloader to page for directory ng-view?

Answer

DWand picture DWand · May 7, 2015

It seems that there are some similar questions here:

Also, there a bunch of modules to work with loading animation at http://ngmodules.org. For example, these:

UPD: I've written a simple solution based on how the angular-loading-bar works. I didn't test it with ng-view, but it seams to work with ui-view. It is not a final solution and have to be polished.

angular.module('ui')

.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('LoadingListener');
}])

.factory('LoadingListener', [ '$q', '$rootScope', function($q, $rootScope) {
    var reqsActive = 0;

    function onResponse() {
        reqsActive--;
        if (reqsActive === 0) {
            $rootScope.$broadcast('loading:completed');
        }
    }

    return {
        'request': function(config) {
            if (reqsActive === 0) {
                $rootScope.$broadcast('loading:started');
            }
            reqsActive++;
            return config;
        },
        'response': function(response) {
            if (!response || !response.config) {
                return response;
            }
            onResponse();
            return response;
        },
        'responseError': function(rejection) {
            if (!rejection || !rejection.config) {
                return $q.reject(rejection);
            }
            onResponse();
            return $q.reject(rejection);
        },
        isLoadingActive : function() {
            return reqsActive === 0;
        }
    };
}])

.directive('loadingListener', [ '$rootScope', 'LoadingListener', function($rootScope, LoadingListener) {

    var tpl = '<div class="loading-indicator" style="position: absolute; height: 100%; width: 100%; background-color: #fff; z-index: 1000">Loading...</div>';

    return {
        restrict: 'CA',
        link: function linkFn(scope, elem, attr) {
            var indicator = angular.element(tpl);
            elem.prepend(indicator);

            elem.css('position', 'relative');
            if (!LoadingListener.isLoadingActive()) {
                indicator.css('display', 'none');
            }

            $rootScope.$on('loading:started', function () {
                indicator.css('display', 'block');
            });
            $rootScope.$on('loading:completed', function () {
                indicator.css('display', 'none');
            });
        }
    };
}]);

It can be used like this:

<section class="content ui-view" loading-listener></section>