题 Angularjs在ajax请求上加载屏幕


使用Angularjs,我需要显示一个加载屏幕(一个简单的微调器),直到ajax请求完成。请使用代码段建议任何想法。


108
2018-06-17 09:20


起源


看一下 stackoverflow.com/questions/15033195/... - Ajay Beniwal
angulartutorial.blogspot.in/2014/04/... - Prashobh
最佳实践是使用 $httpProvider.interceptors 因为它可以处理ajax请求何时开始以及何时结束。这就是为什么 $watch 不再需要检测ajax调用何时开始和结束。 - Frank Myat Thu
你怎么样结账我的工厂 角httpshooter,它可以让您更好地控制加载器和冻结UI - Siddharth


答案:


而不是设置范围变量来指示数据加载状态,最好让指令为您完成所有事情:

angular.module('directive.loading', [])

    .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();
                    }
                });
            }
        };

    }]);

使用此指令,您需要做的就是为任何加载动画元素提供“加载”属性:

<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>

您可以在页面上有多个加载微调器。在哪里以及如何布置这些旋转器取决于您,指令将自动为您打开/关闭它。


199
2017-10-28 06:04



这里使用$ location.path()调用“loading”指令两次。首先是当前页面,然后是目标页面。应该是什么原因? - Sanjay D
优秀。而且,您可以在watch:elm.toggle(v)上使用jquery切换。我有一个$ timeout会话监视器,对我来说,我需要在半秒后显示微调器。我将实现一个带有过渡的css,以更慢地显示微调器。 - Joaozito Polo
如果你得到“elm.show()不是函数”错误,你必须在加载角度之前添加jquery。 - morpheus05
你做scope.watch的方式对我不起作用。我不得不像jzm那样做(通过使用''围绕名称并省略范围前缀)。有什么想法吗? - KingOfHypocrites
如果我需要异步怎么办?加载多个不同的区域? - Vadim Ferderer


这是一个例子。它使用简单的ng-show方法和bool。

HTML

<div ng-show="loading" class="loading"><img src="...">LOADING...</div>
<div ng-repeat="car in cars">
  <li>{{car.name}}</li>
</div>
<button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>

ANGULARJS

  $scope.clickMe = function() {
    $scope.loading = true;
    $http.get('test.json')
      .success(function(data) {
        $scope.cars = data[0].cars;
        $scope.loading = false;
    });
  }

当然你可以将加载框html代码移动到一个指令中,然后在$ scope.loading上使用$ watch。在这种情况下:

HTML

<loading></loading>

ANGULARJS指令

  .directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="..."/>LOADING...</div>',
        link: function (scope, element, attr) {
              scope.$watch('loading', function (val) {
                  if (val)
                      $(element).show();
                  else
                      $(element).hide();
              });
        }
      }
  })

普拉克http://plnkr.co/edit/AI1z21?p=preview


54
2018-06-17 09:47



感谢分享指令示例 - 很高兴有一个通用的方法答案:) - kontur
你做scope.watch的方式适用于我,而接受的答案方法没有。 - KingOfHypocrites
完美的工作。感谢详细解释! - Gene
@jzm这样一个很棒的解决方案,遗憾的是这个解决方案无法使用 ui-router,不知道为什么 - Muhammed
我在我的一个项目中做得很好,在另一个项目中我认为由于重ajax请求 loading=true 不要设置 - alamnaryab


我用 ngProgress 为了这。

在HTML中包含脚本/ css文件后,将“ngProgress”添加到依赖项中。一旦你这样做,你可以设置这样的东西,这将在检测到路线变化时触发。

angular.module('app').run(function($rootScope, ngProgress) {
  $rootScope.$on('$routeChangeStart', function(ev,data) {
    ngProgress.start();
  });
  $rootScope.$on('$routeChangeSuccess', function(ev,data) {
    ngProgress.complete();
  });
});

对于AJAX请求,您可以执行以下操作:

$scope.getLatest = function () {
    ngProgress.start();

    $http.get('/latest-goodies')
         .success(function(data,status) {
             $scope.latest = data;
             ngProgress.complete();
         })
         .error(function(data,status) {
             ngProgress.complete();
         });
};

只需记住在执行此操作之前将“ngProgress”添加到控制器依赖项中。如果您正在执行多个AJAX请求,请在主应用范围内使用增量变量来跟踪AJAX请求何时完成,然后再调用'ngProgress.complete();'。


19
2018-04-04 03:26



哇。这是太棒了。谢谢。 - Fortin


使用pendingRequests不正确,因为如Angular文档中所述,此属性主要用于调试目的。

我建议使用拦截器来知道是否有任何活动的异步调用。

module.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push(function ($q, $rootScope) {
        if ($rootScope.activeCalls == undefined) {
            $rootScope.activeCalls = 0;
        }

        return {
            request: function (config) {
                $rootScope.activeCalls += 1;
                return config;
            },
            requestError: function (rejection) {
                $rootScope.activeCalls -= 1;
                return rejection;
            },
            response: function (response) {
                $rootScope.activeCalls -= 1;
                return response;
            },
            responseError: function (rejection) {
                $rootScope.activeCalls -= 1;
                return rejection;
            }
        };
    });
}]);

然后通过$ watch检查指令中的activeCalls是否为零。

module.directive('loadingSpinner', function ($http) {
    return {
        restrict: 'A',
        replace: true,
        template: '<div class="loader unixloader" data-initialize="loader" data-delay="500"></div>',
        link: function (scope, element, attrs) {

            scope.$watch('activeCalls', function (newVal, oldVal) {
                if (newVal == 0) {
                    $(element).hide();
                }
                else {
                    $(element).show();
                }
            });
        }
    };
});

13
2017-12-12 05:53





最好的方法是使用 响应拦截器 随着自定义 指示。并且可以使用进一步改进该过程 发布/订阅 机制使用 $ rootScope。$广播 & $ rootScope。$上 方法。

整个过程记录在一个写得很好 博客文章,我不会再在这里重复一遍。请参阅该文章以提出您所需的实施方案。


6
2018-01-06 23:22





参考这个答案

https://stackoverflow.com/a/17144634/4146239

对我来说是最好的解决方案,但有一种方法可以避免使用jQuery。

.directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" width="20" height="20" />LOADING...</div>',
        link: function (scope, element, attr) {
              scope.$watch('loading', function (val) {
                  if (val)
                      scope.loadingStatus = 'true';
                  else
                      scope.loadingStatus = 'false';
              });
        }
      }
  })

  .controller('myController', function($scope, $http) {
      $scope.cars = [];
      
      $scope.clickMe = function() {
        scope.loadingStatus = 'true'
        $http.get('test.json')
          .success(function(data) {
            $scope.cars = data[0].cars;
            $scope.loadingStatus = 'false';
        });
      }
      
  });
<body ng-app="myApp" ng-controller="myController" ng-init="loadingStatus='true'">
        <loading ng-show="loadingStatus" ></loading>
  
        <div ng-repeat="car in cars">
          <li>{{car.name}}</li>
        </div>
        <button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>
  
</body>

你需要替换$(element).show();和(元素).show(); with $ scope.loadingStatus ='true';和$ scope.loadingStatus ='false';

然后,您需要使用此变量来设置元素的ng-show属性。


3
2018-04-20 08:11





打字稿和角度实现 

指示

((): void=> {
    "use strict";
    angular.module("app").directive("busyindicator", busyIndicator);
    function busyIndicator($http:ng.IHttpService): ng.IDirective {
        var directive = <ng.IDirective>{
            restrict: "A",
            link(scope: Scope.IBusyIndicatorScope) {
                scope.anyRequestInProgress = () => ($http.pendingRequests.length > 0);
                scope.$watch(scope.anyRequestInProgress, x => {            
                    if (x) {
                        scope.canShow = true;
                    } else {
                        scope.canShow = false;
                    }
                });
            }
        };
        return directive;
    }
})();

范围

   module App.Scope {
        export interface IBusyIndicatorScope extends angular.IScope {
            anyRequestInProgress: any;
            canShow: boolean;
        }
    }  

模板

<div id="activityspinner" ng-show="canShow" class="show" data-busyindicator>
</div>

CSS
#activityspinner
{
    display : none;
}
#activityspinner.show {
    display : block;
    position : fixed;
    z-index: 100;
    background-image : url('data:image/gif;base64,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') 
    -ms-opacity : 0.4;
    opacity : 0.4;
    background-repeat : no-repeat;
    background-position : center;
    left : 0;
    bottom : 0;
    right : 0;
    top : 0;
}

3
2017-11-12 21:24



谢谢你翻译这个! js的例子很难翻译...... - daark
你救了我的一天,谢谢! :) - Lightning3