23

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

但是它们要么必须为每个调用进行配置,要么 - 如果它们按照我的意愿在全局范围内运行 - 只适用于 http-requests,而不适用于服务中使用的 $q-promises。

到目前为止,我看到的全局加载指示器可以使用

$httpProvider.responseInterceptors.push(interceptor);

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

4

4 回答 4

16

尽管我发现它非常复杂、不必要并且可能损坏了,但您可以 装饰$q和覆盖它的defer功能。

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

$rootScope最后,当这个计数器大于 0 时,你添加一个 watch 来监控(比pendingPromissesin$rootScope和 bind like快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;
    }]);
});

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

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

(这不适用于具有隔离范围的指令)

看到它住在这里

于 2013-11-15T16:11:46.833 回答
9

官方文档中有一个很好的例子,适用于当前稳定的 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>0。

于 2013-11-07T00:11:05.480 回答
5

由于 OP 要求,这是基于我们用于当前正在开发的应用程序的方法。此方法不会改变的行为$q,而是添加了一个非常简单的API 来处理需要某种视觉指示的 Promise。尽管这需要在使用它的每个地方进行修改,但它只是一个衬里。

用法

例如,有一个服务ajaxIndicator知道如何更新 UI 的一部分。每当类似 Promise 的对象需要提供指示直到 Promise 被解决时,我们使用:

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

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

// $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
    };
});
于 2013-11-14T16:52:15.090 回答
1

几周前我有同样的大问题,我碰巧做了一些指令来表示操作按钮的加载状态和 ng-repeat 内容加载。

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

我希望它有所帮助。

于 2013-07-18T21:28:49.497 回答