如何在angularjs-app中为每个异步操作(使用$ q)创建一个加载指示器

时间:2013-07-05 18:13:48

标签: javascript asynchronous angularjs

我知道有几种方法可以在角度js中加载指标(例如:https://gist.github.com/maikeldaloo/5140733)。

但是他们要么必须为每一次调用配置,要么 - 如果他们按照我的意愿全局行动 - 只需应用于http请求,但不应用于服务中使用的$ q-promises。

到目前为止,我见过的全球负载指标与

一起使用
$httpProvider.responseInterceptors.push(interceptor);

$q有类似$qProvider.reponseInterceptors的内容吗?如果没有,那么实现这种功能最方便的方法是什么?例如,是否可以使用某种类型的装饰器图案?

4 个答案:

答案 0 :(得分:16)

虽然我发现它非常复杂,不必要且可能已损坏,但可以装饰$q并覆盖其defer功能。

每次有人要求新的defer()时,它会运行您自己的版本,这也会增加一个计数器。在分发defer对象之前,您注册finally回调(仅Angular 1.2.0,但always也可能适合)以减少计数器。

最后,您向$rootScope添加一个监视,以监控此计数器何时大于0(比pendingPromisses中的$rootScope更快并且像ng-show="pendingPromisses > 0"那样绑定)。< / p>

app.config(function($provide) {
    $provide.decorator('$q', ['$delegate', '$rootScope', function($delegate, $rootScope) {
      var pendingPromisses = 0;
      $rootScope.$watch(
        function() { return pendingPromisses > 0; }, 
        function(loading) { $rootScope.loading = loading; }
      );
      var $q = $delegate;
      var origDefer = $q.defer;
      $q.defer = function() {
        var defer = origDefer();
        pendingPromisses++;
        defer.promise.finally(function() {
          pendingPromisses--;
        });
        return defer;
      };
      return $q;
    }]);
});

然后,绑定到继承自$rootScope的范围的视图可以具有:

<span ng-show="loading">Loading, please wait</span>

(这在具有隔离范围的指令中不起作用)

直播here

答案 1 :(得分:8)

在为当前稳定1.2.0工作的官方文档中有一个很好的例子。

http://docs.angularjs.org/api/ng.$http(页面的前四分之一,搜索拦截器)

我对这些文档的提取引出了我的解决方案:

angular.module('RequestInterceptor', [])
  .config(function ($httpProvider) {
    $httpProvider.interceptors.push('requestInterceptor');
  })
  .factory('requestInterceptor', function ($q, $rootScope) {
    $rootScope.pendingRequests = 0;
    return {
           'request': function (config) {
                $rootScope.pendingRequests++;
                return config || $q.when(config);
            },

            'requestError': function(rejection) {
                $rootScope.pendingRequests--;
                return $q.reject(rejection);
            },

            'response': function(response) {
                $rootScope.pendingRequests--;
                return response || $q.when(response);
            },

            'responseError': function(rejection) {
                $rootScope.pendingRequests--;
                return $q.reject(rejection);
            }
        }
    });

然后,您可以在ng-show表达式中使用pendingRequests&gt; 0。

答案 2 :(得分:5)

自OP提出要求以来,这是基于我们正在使用的应用程序的方法。此方法 NOT 更改$q的行为,而是添加非常简单的 API来处理需要某种可视指示的promise。虽然这需要在每个使用的地方进行修改,但它只是一个单行。

用法

有一项服务,比如ajaxIndicator,知道如何更新部分UI。每当类似承诺的对象需要提供指示,直到承诺得到解决,我们使用:

// $http example:
var promise = $http.get(...);
ajaxIndicator.indicate(promise); // <--- this line needs to be added

如果您不想继续提及承诺:

// $http example without keeping the reference:
ajaxIndicator.indicate($http.get(...));

或者使用资源:

var rc = $resource(...);
...
$scope.obj = rc.get(...);
ajaxIndicator.indicate($scope.obj);

(注意:对于Angular 1.2,这需要tweeking,因为资源对象上没有$then()。)

现在在根模板中,您必须将指标绑定到$rootScope.ajaxActive,例如:

<div class="ajax-indicator" ng-show="ajaxActive"></div>

实施

(从我们的源代码修改。)警告:此实现不考虑嵌套调用! (我们的要求是UI阻塞,因此我们不希望嵌套调用;如果感兴趣,我可以尝试增强此代码。)

app.service("ajaxIndicator", ["$rootScope"], function($rootScope) {
    "use strict";

    $rootScope.ajaxActive = false;

    function indicate(promise) {
        if( !$rootScope.ajaxActive ) {
            $rootScope.ajaxActive = true;
            $rootScope.$broadcast("ajax.active"); // OPTIONAL
            if( typeof(promise) === "object" && promise !== null ) {
                if( typeof(promise.always) === "function" ) promise.always(finished);
                else if( typeof(promise.then) === "function" ) promise.then(finished,finished);
                else if( typeof(promise.$then) === "function" ) promise.$then(finished,finished);
            }
        }
    }

    function finished() {
        $rootScope.ajaxActive = false;
    }

    return {
        indicate: indicate,
        finished: finished
    };
});

答案 3 :(得分:1)

几个星期前我遇到了同样的大问题,我碰巧做了一些指令来表示动作按钮和ng-repeat内容加载的加载状态。

我只是花了一些时间把它推到了github上: https://github.com/ocolot/angularjs_loading_buttons

我希望它有所帮助。