javascript 如何为 angularjs-app 中的每个异步操作(使用 $q)制作加载指示器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17494732/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make a loading indicator for every asynchronous action (using $q) in an angularjs-app
提问by hugo der hungrige
I know there are several approaches to loading-indicators in angular js (this one, for example: https://gist.github.com/maikeldaloo/5140733).
我知道在 angular js 中有几种加载指标的方法(例如:https: //gist.github.com/maikeldaloo/5140733)。
But they either have to be configured for every single call, or - if they act globally, as I want - just apply to http-requests, but not to $q-promises being used in services.
但是它们要么必须为每次调用进行配置,要么 - 如果它们在全局范围内运行,如我所愿 - 仅适用于 http-requests,而不适用于服务中使用的 $q-promises。
The global loading indicators, I've seen so far, work with
到目前为止,我看到的全局加载指标与
$httpProvider.responseInterceptors.push(interceptor);
Is there something similiar for $q
, like a $qProvider.reponseInterceptors
? And if not, what would be the most convenient way to implement such a functionality? Is it possible to use a decorator-pattern of some kind for example?
有没有类似的东西$q
,比如$qProvider.reponseInterceptors
?如果没有,实现这种功能的最方便的方法是什么?例如,是否可以使用某种装饰器模式?
采纳答案by Kos Prov
Although I find it very complicated, unnecessary and probably broken, you coulddecorate $q
and override its defer
function.
虽然我觉得它非常复杂、不必要并且可能已经损坏,但您可以装饰$q
和覆盖它的defer
功能。
Every timesomeone asks for a new defer()
it runs your own version which also increments a counter. Before handing out the defer
object, you register a finally
callback (Angular 1.2.0 only but always
may fit, too) to decrement the counter.
每次有人要求新的时,defer()
它都会运行您自己的版本,这也会增加一个计数器。在分发defer
对象之前,您注册一个finally
回调(仅always
适用于Angular 1.2.0,但也可能适用)以递减计数器。
Finally, you add a watch to $rootScope
to monitor when this counter is greater than 0 (faster than having pendingPromisses
in $rootScope
and bind like ng-show="pendingPromisses > 0"
).
最后,$rootScope
当这个计数器大于 0 时,你添加一个监视来监视(比pendingPromisses
in$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;
}]);
});
Then, view bound to a scope that inherits from $rootScope
can have:
然后,绑定到继承自范围的视图$rootScope
可以具有:
<span ng-show="loading">Loading, please wait</span>
(this won't work in directives with isolate scopes)
(这不适用于具有隔离范围的指令)
See it live here.
看到它住在这里。
回答by angabriel
There is a good example in the official documentation working for the current stable 1.2.0.
官方文档中有一个很好的例子,适用于当前稳定的 1.2.0。
http://docs.angularjs.org/api/ng.$http(top quarter of the page, search for Interceptors)
http://docs.angularjs.org/api/ng.$http(页面顶部四分之一,搜索拦截器)
My extraction of these documentation lead me to this solution:
我对这些文档的提取使我找到了这个解决方案:
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);
}
}
});
You might then use pendingRequests>0 in an ng-show expression.
然后,您可以在 ng-show 表达式中使用 pendingRequests>0。
回答by Nikos Paraskevopoulos
Since requested by the OP, this is based on the method we are using for the app we are currently working on. This method does NOTchange the behaviour of $q
, rather adds a very simpleAPI to handle promises that need some kind of visual indication. Although this needs modification in every place it is used, it is only a one-liner.
由于 OP 的要求,这是基于我们用于当前正在开发的应用程序的方法。此方法不会不改变的行为$q
,而增加了一个非常简单的API需要某种视觉指示的手柄承诺。虽然这在每个使用它的地方都需要修改,但它只是一个划线。
Usage
用法
There is a service, say ajaxIndicator
, that knows how to update a portion of the UI. Whenever a promise-like object needs to provide indication until the promise is resolved we use:
例如,有一个服务ajaxIndicator
知道如何更新 UI 的一部分。每当类似承诺的对象需要提供指示直到承诺解决时,我们使用:
// $http example:
var promise = $http.get(...);
ajaxIndicator.indicate(promise); // <--- this line needs to be added
If you do not want to keep a reference to the promise:
如果您不想保留对承诺的引用:
// $http example without keeping the reference:
ajaxIndicator.indicate($http.get(...));
Or with a resource:
或者使用资源:
var rc = $resource(...);
...
$scope.obj = rc.get(...);
ajaxIndicator.indicate($scope.obj);
(NOTE: For Angular 1.2 this would need tweeking, as there is no $then()
on the resource object.)
(注意:对于 Angular 1.2,这需要 tweeking,因为$then()
资源对象上没有。)
Now in the root template, you will have to bind the indicator to $rootScope.ajaxActive
, e.g.:
现在在根模板中,您必须将指标绑定到$rootScope.ajaxActive
,例如:
<div class="ajax-indicator" ng-show="ajaxActive"></div>
Implementation
执行
(Modified from our source.) WARNING: This implementation does not take into account nested calls! (Our requirements called for UI blocking, so we do not expect nested calls; if interested I could try to enhance this code.)
(从我们的源代码修改。)警告:此实现不考虑嵌套调用!(我们的要求要求 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
};
});
回答by ocolot
I had the same big question few weeks ago and I happen to make some directives to represent the loading state on the action buttons and ng-repeat content loading.
几周前我遇到了同样的大问题,我碰巧制定了一些指令来表示操作按钮上的加载状态和 ng-repeat 内容加载。
I just spent some time and pushed it on github: https://github.com/ocolot/angularjs_loading_buttons
我只是花了一些时间,把它推到了github上:https: //github.com/ocolot/angularjs_loading_buttons
I hope it helps.
我希望它有帮助。