AngularJS - 检测所有 $http() 请求何时完成

26

好的,我有很多在应用程序代码中所有地方都有的$http()调用,

我想知道是否有任何方法 / 最佳实践来检测应用程序周围的所有$http()何时完成(无论它们返回什么成功/错误,只需知道是否完成)?

这样我就可以显示加载GIF,直到它们加载完毕?

谢谢


3
人们已经建立了类似的东西。请查看http://codingsmackdown.tv/blog/2013/01/02/using-response-interceptors-to-show-and-hide-a-loading-widget/,http://codingsmackdown.tv/blog/2013/04/20/using-response-interceptors-to-show-and-hide-a-loading-widget-redux/。 - Chandermani
3个回答

53

就这样做:

angular.module('app').factory('httpInterceptor', ['$q', '$rootScope',
    function ($q, $rootScope) {
        var loadingCount = 0;

        return {
            request: function (config) {
                if(++loadingCount === 1) $rootScope.$broadcast('loading:progress');
                return config || $q.when(config);
            },

            response: function (response) {
                if(--loadingCount === 0) $rootScope.$broadcast('loading:finish');
                return response || $q.when(response);
            },

            responseError: function (response) {
                if(--loadingCount === 0) $rootScope.$broadcast('loading:finish');
                return $q.reject(response);
            }
        };
    }
]).config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push('httpInterceptor');
}]);

然后在任何地方绑定到 $rootScope 的事件(最好在指令中使用):

$rootScope.$on('loading:progress', function (){
    // show loading gif
});

$rootScope.$on('loading:finish', function (){
    // hide loading gif
});

伟大的解决方案!我在想是否需要检测指令HTTP。例如,这将捕获每个HTTP,是吗? - itsme
我不确定是否要将http()分成阻塞和非阻塞的,以便进行布局,但这是我的问题。 - itsme
1
@sbaaaang 它将捕获所有使用 $http 进行的 Angular Ajax 请求。当然,如果您使用 $.ajax(或任何其他 xmlhttprequest),它将无法捕获。 - karaxuna
2
这在Angular 1.4中不起作用。loading:finish会在每个xhr请求完成时被调用,而不是所有请求都完成时被调用。 - Northstrider
救了我的一天,非常感谢。 - Nischal Kumar BC
显示剩余4条评论

10

如果您通过 $q.all 执行所有请求,则可以实现此操作。

$scope.request1 = $http.get('request1URL', {cache: false});
$scope.request2 = $http.get('request2URL', {'cache': false});
$scope.loading = true;

$q.all([$scope.request1, $scope.request2]).then(function(values) {
     $scope.loading = false;
     // Do whatever you want
     // values[0], values[1]
});

使用ng-if,您可以显示和隐藏加载的gif

如果您使用不同的$http调用,则在$rootScope中具有一个countarray,并在每次完成$http时更新它们。根据countarray.length启用加载gif


谢谢,期待我实际需要的解决方案。 - itsme
如果您想要全局执行此操作,而不必在每个控制器/服务中实现$q.all逻辑,该怎么办? - Northstrider

0

使用@karaxuna的答案,我像下面这样实现了我的解决方案。 Plunker

<!doctype html>
<html ng-app="ui.bootstrap.demo">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-animate.js"></script>
    <script src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.js"></script>
    <script>
    var app = angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);

    app.config(['$httpProvider', function ($httpProvider) {
        $httpProvider.interceptors.push(function ($q, $rootScope, $templateCache) {
            var AjaxLoadingCount = 0;
            return {
                request: function (config) {
                    console.log("[config.interceptorService] request config", config);                  
                    if (++AjaxLoadingCount === 1)
                    {
                        if (!$templateCache.get(config.url)) {
                            $rootScope.$broadcast('AjaxLoading:Progress');
                        }
                    }
                    return config || $q.when(config);
                },
                requestError: function (rejection) {
                    console.log("[config.interceptorService] requestError rejection", rejection);
                    if(--AjaxLoadingCount === 0) $rootScope.$broadcast('AjaxLoading:Finish');
                    return $q.reject(rejection);
                },
                response: function (response) {
                    console.log("[config.interceptorService] response response", response); 
                    if(--AjaxLoadingCount === 0) $rootScope.$broadcast('AjaxLoading:Finish');
                    return response || $q.when(response);                   
                },
                responseError: function (rejection) {
                    console.log("[config.interceptorService] responseError rejection", rejection);
                    if(--AjaxLoadingCount === 0) $rootScope.$broadcast('AjaxLoading:Finish');
                    return $q.reject(rejection);
                }
            };
        });
    }]);

    app.factory('ajaxFactory', function ($http) {

        var ajaxFactory = {};
        ajaxFactory.LoadData = function () {            
            return $http({
                method: "GET",
                url: "http://www.w3schools.com/angular/customers.php",
            });
        };
        return ajaxFactory;

    });

    app.controller('ModalDemoCtrl', function ($scope, ajaxFactory) {
        $scope.doAjax = function () {

            ajaxFactory.LoadData()
            .success(function (data, status, headers, config) {
                console.log("Data vai factory\n", JSON.stringify(data));
                console.log("Loading finish.");             
            }).error(function (data, status, headers, config) {

            });
        };
    });

    app.directive("ajaxLoadingDirective", function ($uibModal) {
        var modalInstance;
        return {
            restrict: 'EA', //E = element, A = attribute, C = class, M = comment            
            scope: true,
            link: function (scope, elem, attrs) {
                console.log("AjaxLoadingDirective.AjaxLoading:Progress");
                scope.$on("AjaxLoading:Progress", function () {                 
                    modalInstance = $uibModal.open({
                        animation: true,
                        templateUrl: 'myModalContent.html'
                    });
                });
                return scope.$on("AjaxLoading:Finish", function () {
                    console.log("AjaxLoadingDirective.AjaxLoading:Finish");
                    if(modalInstance === undefined) return;
                    modalInstance.dismiss();
                });
            }
        }
    });

    </script>
    <link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <div ng-controller="ModalDemoCtrl">
        <script type="text/ng-template" id="myModalContent.html">
            Loading data .......................            
        </script>
        <button type="button" class="btn btn-default" ng-click="doAjax()">Execute Ajax</button>
        <div ajax-loading-directive ></div>
    </div>
  </body>
</html>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接