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

hug*_*ige 22 javascript asynchronous angularjs

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

但是它们要么必须为每一个呼叫配置,要么 - 如果它们按照我的意愿全局行动 - 只应用于http请求,而不是应用于服务中使用的$ q-promises.

到目前为止,我见过的全球负荷指标都适用

$httpProvider.responseInterceptors.push(interceptor);
Run Code Online (Sandbox Code Playgroud)

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

Kos*_*rov 16

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

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

最后,添加一个$rootScope监视器来监视此计数器何时大于0(比pendingPromisses进入$rootScope和绑定更快ng-show="pendingPromisses > 0").

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;
    }]);
});
Run Code Online (Sandbox Code Playgroud)

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

<span ng-show="loading">Loading, please wait</span>
Run Code Online (Sandbox Code Playgroud)

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

在这里看到它.

  • 我认为`$ q`是一个通用的异步编程实用程序,通常不能指示*某些内容正在加载*.它可以由其他(第三方)模块在内部使用,这些模块不了解您的约定.然后,你回到原点.加载微调器通常意味着"等待服务器"(又名`$ httpProvider.interceptors`).其他*应该*非常快. (4认同)

ang*_*iel 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);
            }
        }
    });
Run Code Online (Sandbox Code Playgroud)

然后,您可以在ng-show表达式中使用pendingRequests> 0.


Nik*_*los 5

自OP请求以来,这是基于我们正在使用的应用程序的方法.此方法不会改变的行为$q,而增加了一个非常简单的 API来处理需要某种形式的视觉指示的承诺.虽然这需要在每个使用的地方进行修改,但它只是一个单行.

用法

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

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

如果您不想保留对承诺的引用:

// $http example without keeping the reference:
ajaxIndicator.indicate($http.get(...));
Run Code Online (Sandbox Code Playgroud)

或者使用资源:

var rc = $resource(...);
...
$scope.obj = rc.get(...);
ajaxIndicator.indicate($scope.obj);
Run Code Online (Sandbox Code Playgroud)

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

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

<div class="ajax-indicator" ng-show="ajaxActive"></div>
Run Code Online (Sandbox Code Playgroud)

履行

(从我们的源代码修改.)警告:此实现不考虑嵌套调用!(我们的要求是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
    };
});
Run Code Online (Sandbox Code Playgroud)