在angular度显示$ http请求期间显示Spinner GIF
我正在使用angular度的$http
服务来发出ajax请求。
如何在ajax请求期间显示加载器gif?
我没有在文档中看到任何ajaxstartevent
或类似的事件。
以下是目前的 AngularJS咒语:
angular.module('SharedServices', []) .config(function ($httpProvider) { $httpProvider.responseInterceptors.push('myHttpInterceptor'); var spinnerFunction = function (data, headersGetter) { // todo start the spinner here //alert('start spinner'); $('#mydiv').show(); return data; }; $httpProvider.defaults.transformRequest.push(spinnerFunction); }) // register the interceptor as a service, intercepts ALL angular ajax http calls .factory('myHttpInterceptor', function ($q, $window) { return function (promise) { return promise.then(function (response) { // do something on success // todo hide the spinner //alert('stop spinner'); $('#mydiv').hide(); return response; }, function (response) { // do something on error // todo hide the spinner //alert('stop spinner'); $('#mydiv').hide(); return $q.reject(response); }); }; }); //regular angular initialization continued below.... angular.module('myApp', [ 'myApp.directives', 'SharedServices']). //.......
这是其余的(HTML / CSS)….使用
$('#mydiv').show(); $('#mydiv').hide();
切换它。 注意:以上是用于angular度模块在开始的职位
#mydiv { position:absolute; top:0; left:0; width:100%; height:100%; z-index:1000; background-color:grey; opacity: .8; } .ajax-loader { position: absolute; left: 50%; top: 50%; margin-left: -32px; /* -1 * image width / 2 */ margin-top: -32px; /* -1 * image height / 2 */ display: block; } <div id="mydiv"> <img src="lib/jQueryhttp://img.dovov.comajax-loader.gif" class="ajax-loader"/> </div>
这真的取决于你的具体用例,但一个简单的方法会遵循这样的模式:
.controller('MainCtrl', function ( $scope, myService ) { $scope.loading = true; myService.get().then( function ( response ) { $scope.items = response.data; }, function ( response ) { // TODO: handle the error somehow }).finally(function() { // called no matter success or failure $scope.loading = false; }); });
然后在你的模板中对它做出反应:
<div class="spinner" ng-show="loading"></div> <div ng-repeat="item in items>{{item.name}}</div>
这是一个使用directive
和ng-hide
。
这将通过angular度的$http
服务在所有调用中显示加载器。
在模板中:
<div class="loader" data-loading></div>
指示:
angular.module('app') .directive('loading', ['$http', function ($http) { return { restrict: 'A', link: function (scope, element, attrs) { scope.isLoading = function () { return $http.pendingRequests.length > 0; }; scope.$watch(scope.isLoading, function (value) { if (value) { element.removeClass('ng-hide'); } else { element.addClass('ng-hide'); } }); } }; }]);
通过在元素上使用ng-hide
类,可以避免使用jquery。
自定义:添加一个interceptor
如果您创build加载拦截器,则可以根据条件显示/隐藏加载器。
指示:
var loadingDirective = function ($rootScope) { return function ($scope, element, attrs) { $scope.$on("loader_show", function () { return element.removeClass('ng-hide'); }); return $scope.$on("loader_hide", function () { return element.addClass('ng-hide'); }); }; };
拦截器:
- 例如:当
response.background === true;
时不显示spinner
response.background === true;
- 拦截
request
和/或response
设置$rootScope.$broadcast("loader_show");
或$rootScope.$broadcast("loader_hide");
有关编写拦截器的更多信息
如果您使用的是ngResource,则对象的$ resolved属性对于加载器非常有用:
对于一个资源如下:
var User = $resource('/user/:id', {id:'@id'}); var user = User.get({id: 1})
您可以将加载器链接到资源对象的$ resolved属性:
<div ng-hide="user.$resolved">Loading ...</div>
https://github.com/wongatech/angular-http-loader是一个很好的项目。;
这里的例子http://wongatech.github.io/angular-http-loader/
下面的代码显示了请求发生时的模板示例/ loader.tpl.html。
<div ng-http-loader template="example/loader.tpl.html"></div>
刚刚发现了根据一些asynchronous调用显示一个小加载器的angular-busy
指令。
例如,如果您必须进行GET
,请参阅$scope
的promise,
$scope.req = $http.get('http://google.fr');
并像这样调用它:
<div cg-busy="req"></div>
这是GitHub 。
您也可以使用bower
安装(不要忘记更新您的项目依赖关系):
bower install angular-busy --save
如果你在一个服务/工厂内打包你的API调用,那么你可以在那里跟踪加载计数器(每个回答和@JMaylin出色的同时提示),并通过指令引用加载计数器。 或者其任何组合。
API WRAPPER
yourModule .factory('yourApi', ['$http', function ($http) { var api = {} //#region ------------ spinner ------------- // ajax loading counter api._loading = 0; /** * Toggle check */ api.isOn = function () { return api._loading > 0; } /** * Based on a configuration setting to ignore the loading spinner, update the loading counter * (for multiple ajax calls at one time) */ api.spinner = function(delta, config) { // if we haven't been told to ignore the spinner, change the loading counter // so we can show/hide the spinner if (NG.isUndefined(config.spin) || config.spin) api._loading += delta; // don't let runaway triggers break stuff... if (api._loading < 0) api._loading = 0; console.log('spinner:', api._loading, delta); } /** * Track an ajax load begin, if not specifically disallowed by request configuration */ api.loadBegin = function(config) { api.spinner(1, config); } /** * Track an ajax load end, if not specifically disallowed by request configuration */ api.loadEnd = function (config) { api.spinner(-1, config); } //#endregion ------------ spinner ------------- var baseConfig = { method: 'post' // don't need to declare `spin` here } /** * $http wrapper to standardize all api calls * @param args stuff sent to request * @param config $http configuration, such as url, methods, etc */ var callWrapper = function(args, config) { var p = angular.extend(baseConfig, config); // override defaults // fix for 'get' vs 'post' param attachment if (!angular.isUndefined(args)) p[p.method == 'get' ? 'params' : 'data'] = args; // trigger the spinner api.loadBegin(p); // make the call, and turn of the spinner on completion // note: may want to use `then`/`catch` instead since `finally` has delayed completion if down-chain returns more promises return $http(p)['finally'](function(response) { api.loadEnd(response.config); return response; }); } api.DoSomething = function(args) { // yes spinner return callWrapper(args, { cache: true }); } api.DoSomethingInBackground = function(args) { // no spinner return callWrapper(args, { cache: true, spin: false }); } // expose return api; });
斯皮纳指示
(function (NG) { var loaderTemplate = '<div class="ui active dimmer" data-ng-show="hasSpinner()"><div class="ui large loader"></div></div>'; /** * Show/Hide spinner with ajax */ function spinnerDirective($compile, api) { return { restrict: 'EA', link: function (scope, element) { // listen for api trigger scope.hasSpinner = api.isOn; // attach spinner html var spin = NG.element(loaderTemplate); $compile(spin)(scope); // bind+parse element.append(spin); } } } NG.module('yourModule') .directive('yourApiSpinner', ['$compile', 'yourApi', spinnerDirective]); })(angular);
用法
<div ng-controller="myCtrl" your-api-spinner> ... </div>
对于页面加载和模式,最简单的方法是使用ng-show direcive并使用其中一个范围数据variables。 像ng-show =“angular.isUndefined(scope.data.someobject)”。 由于数据未定,微调将显示。 一旦服务返回数据和范围填充,微调将被隐藏。
这是最简单的方法来添加一个微调我猜: –
你可以使用ng-show与这些美丽的纺纱厂的任何一个的div标签http://tobiasahlin.com/spinkit/ {{这不是我的页面}}
然后你可以使用这种逻辑
//ajax start $scope.finderloader=true; $http({ method :"POST", url : "your URL", data: { //your data } }).then(function mySucces(response) { $scope.finderloader=false; $scope.search=false; $scope.myData =response.data.records; }); //ajax end
<div ng-show="finderloader" class=spinner></div> //add this in your HTML at right place
Based on Josh David Miller response: <body> <header> </header> <div class="spinner" ng-show="loading"> <div class="loader" ></div> </div> <div ng-view=""></div> <footer> </footer> </body>
添加这个CSS:
.loader { border: 16px solid #f3f3f3; border-radius: 50%; border-top: 16px solid #3498db; border-bottom : 16px solid black; width: 80px; height: 80px; -webkit-animation: spin 2s linear infinite; animation: spin 2s linear infinite; position: absolute; top: 45%; left: 45%; } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .spinner{ width: 100%; height: 100%; z-index: 10000; position: absolute; top: 0; left: 0; margin: 0 auto; text-align: center; vertical-align: middle; background: white; opacity: 0.6; }
而只是在你的angular度添加:
$ rootScope.loading = false; $ rootScope.loading = true; – > $ http.get结束时。
这适用于我:
HTML:
<div id="loader" class="ng-hide" ng-show="req.$$state.pending"> <img class="ajax-loader" width="200" height="200" src="http://img.dovov.comspinner.gif" /> </div>
angular度:
$scope.req = $http.get("/admin/view/"+id).success(function(data) { $scope.data = data; });
虽然从$ http返回的承诺正在等待,但ng-show将评估为“truthy”。 一旦承诺解决了,这将自动更新……这正是我们想要的。
用下面的截取器在http请求上显示加载条
'use strict'; appServices.factory('authInterceptorService', ['$q', '$location', 'localStorage','$injector','$timeout', function ($q, $location, localStorage, $injector,$timeout) { var authInterceptorServiceFactory = {}; var requestInitiated; //start loading bar var _startLoading = function () { console.log("error start loading"); $injector.get("$ionicLoading").show(); } //stop loading bar var _stopLoading = function () { $injector.get("$ionicLoading").hide(); } //request initiated var _request = function (config) { requestInitiated = true; _startLoading(); config.headers = config.headers || {}; var authDataInitial = localStorage.get('authorizationData'); if (authDataInitial && authDataInitial.length > 2) { var authData = JSON.parse(authDataInitial); if (authData) { config.headers.Authorization = 'Bearer ' + authData.token; } } return config; } //request responce error var _responseError = function (rejection) { _stopLoading(); if (rejection.status === 401) { $location.path('/login'); } return $q.reject(rejection); } //request error var _requestError = function (err) { _stopLoading(); console.log('Request Error logging via interceptor'); return err; } //request responce var _response = function(response) { requestInitiated = false; // Show delay of 300ms so the popup will not appear for multiple http request $timeout(function() { if(requestInitiated) return; _stopLoading(); console.log('Response received with interceptor'); },300); return response; } authInterceptorServiceFactory.request = _request; authInterceptorServiceFactory.responseError = _responseError; authInterceptorServiceFactory.requestError = _requestError; authInterceptorServiceFactory.response = _response; return authInterceptorServiceFactory; }]);
在@bulltorious中分享我的版本,为更新的angular色版本(我使用版本1.5.8和这个代码)进行了更新,并且还结合了@ JMaylin的使用计数器的思想,以便对多个同时发生的请求选项跳过显示请求的animationless于一些最小的毫秒数:
var app = angular.module('myApp'); var BUSY_DELAY = 1000; // Will not show loading graphic until 1000ms have passed and we are still waiting for responses. app.config(function ($httpProvider) { $httpProvider.interceptors.push('busyHttpInterceptor'); }) .factory('busyHttpInterceptor', ['$q', '$timeout', function ($q, $timeout) { var counter = 0; return { request: function (config) { counter += 1; $timeout( function () { if (counter !== 0) { angular.element('#busy-overlay').show(); } }, BUSY_DELAY); return config; }, response: function (response) { counter -= 1; if (counter === 0) { angular.element('#busy-overlay').hide(); } return response; }, requestError: function (rejection) { counter -= 1; if (counter === 0) { angular.element('#busy-overlay').hide(); } return rejection; }, responseError: function (rejection) { counter -= 1; if (counter === 0) { angular.element('#busy-overlay').hide(); } return rejection; } } }]);
.factory('authHttpResponseInterceptor', ['$q', function ($q) { return { request: function(config) { angular.element('#spinner').show(); return config; }, response : function(response) { angular.element('#spinner').fadeOut(3000); return response || $q.when(response); }, responseError: function(reason) { angular.element('#spinner').fadeOut(3000); return $q.reject(reason); } }; }]); .config(['$routeProvider', '$locationProvider', '$translateProvider', '$httpProvider', function ($routeProvider, $locationProvider, $translateProvider, $httpProvider) { $httpProvider.interceptors.push('authHttpResponseInterceptor'); } ]); in your Template <div id="spinner"></div> css #spinner, #spinner:after { border-radius: 50%; width: 10em; height: 10em; background-color: #A9A9A9; z-index: 10000; position: absolute; left: 50%; bottom: 100px; } @-webkit-keyframes load8 { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } @keyframes load8 { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
用这个代码创build指令:
$scope.$watch($http.pendingRequests, toggleLoader); function toggleLoader(status){ if(status.length){ element.addClass('active'); } else { element.removeClass('active'); } }
显示不同url更改之间加载的另一个解决scheme是:
$rootScope.$on('$locationChangeStart', function() { $scope.loading++; }); $rootScope.$on('$locationChangeSuccess', function() { $timeout(function() { $scope.loading--; }, 300); });
然后在标记中,用ng-show="loading"
切换微调。
如果要在ajax请求上显示它,只需在请求开始时添加$scope.loading++
,并在结束时添加$scope.loading--
。
你也可以尝试这样的事情:
创build指令:
myApp.directive('loader', function () { return { restrict: 'A', scope: {cond: '=loader'}, template: '<span ng-if="isLoading()" class="soft"><span class="fa fa-refresh fa-spin"></span></span>', link: function (scope) { scope.isLoading = function() { var ret = scope.cond === true || ( scope.cond && scope.cond.$$state && angular.isDefined(scope.cond.$$state.status) && scope.cond.$$state.status === 0 ); return ret; } } }; });
然后你添加这样的mainCtrl
// Return TRUE if some request is LOADING, else return FALSE $scope.isLoading = function() { return $http.pendingRequests.length > 0; };
而HTML可以像这样:
<div class="buttons loader"> <span class="icon" loader="isLoading()"></span> </div>
以下方式将logging所有请求,并且只在所有请求完成后才隐藏:
app.factory('httpRequestInterceptor', function(LoadingService, requestCount) { return { request: function(config) { if (!config.headers.disableLoading) { requestCount.increase(); LoadingService.show(); } return config; } }; }).factory('httpResponseInterceptor', function(LoadingService, $timeout, error, $q, requestCount) { function waitAndHide() { $timeout(function() { if (requestCount.get() === 0){ LoadingService.hide(); } else{ waitAndHide(); } }, 300); } return { response: function(config) { requestCount.descrease(); if (requestCount.get() === 0) { waitAndHide(); } return config; }, responseError: function(config) { requestCount.descrease(); if (requestCount.get() === 0) { waitAndHide(); } var deferred = $q.defer(); error.show(config.data, function() { deferred.reject(config); }); return deferred.promise; } }; }).factory('requestCount', function() { var count = 0; return { increase: function() { count++; }, descrease: function() { if (count === 0) return; count--; }, get: function() { return count; } }; })
由于position:fixed的function最近改变了,所以我很难在所有元素上显示gif loader,所以我不得不使用angular的内置jQuery 。
HTML
<div ng-controller="FetchController"> <div id="spinner"></div> </div>
CSS
#spinner {display: none} body.spinnerOn #spinner { /* body tag not necessary actually */ display: block; height: 100%; width: 100%; background: rgba(207, 13, 48, 0.72) url(img/loader.gif) center center no-repeat; position: fixed; top: 0; left: 0; z-index: 9999; } body.spinnerOn main.content { position: static;} /* and whatever content needs to be moved below your fixed loader div */
调节器
app.controller('FetchController', ['$scope', '$http', '$templateCache', '$location', '$q', function($scope, $http, $templateCache, $location, $q) { angular.element('body').addClass('spinnerOn'); // add Class to body to show spinner $http.post( // or .get( // your data here }) .then(function (response) { console.info('success'); angular.element('body').removeClass('spinnerOn'); // hide spinner return response.data; }, function (response) { console.info('error'); angular.element('body').removeClass('spinnerOn'); // hide spinner }); })
希望这可以帮助 :)
所有的答案都是复杂的,或者需要为每个请求设置一些variables,这是非常错误的做法,如果我们知道DRY的概念。 这里简单的拦截器的例子,我把鼠标放在ajax启动时的等待,当ajax结束时将它设置为auto。
$httpProvider.interceptors.push(function($document) { return { 'request': function(config) { // here ajax start // here we can for example add some class or show somethin $document.find("body").css("cursor","wait"); return config; }, 'response': function(response) { // here ajax ends //here we should remove classes added on request start $document.find("body").css("cursor","auto"); return response; } }; });
代码必须在应用程序configurationapp.config
添加。 我展示了如何在加载状态下更改鼠标,但在那里可以显示/隐藏任何加载器内容,或添加,删除一些显示加载器的CSS类。
拦截器将在每个Ajax调用中运行,所以不需要在每个http调用上创build特殊的布尔variables($ scope.loading = true / false等) 。
这是我的实现,就像一个ng-show和一个请求计数器一样简单。
它使用一个新的服务的所有请求$ http:
myApp.service('RqstSrv', [ '$http', '$rootScope', function($http, $rootScope) { var rqstService = {}; rqstService.call = function(conf) { $rootScope.currentCalls = !isNaN($rootScope.currentCalls) ? $rootScope.currentCalls++ : 0; $http(conf).then(function APICallSucceed(response) { // Handle success }, function APICallError(response) { // Handle error }).then(function() { $rootScope.currentCalls--; }); } } ]);
然后,您可以使用您的加载器基于当前调用的数量:
<img data-ng-show="currentCalls > 0" src="images/ajax-loader.gif"/>
这是我的解决scheme,我觉得很多easer其他张贴在这里。 不知道这是多么“美丽”,但它解决了我所有的问题
我有一个称为“加载”的CSS样式
.loading { display: none; }
加载div的html可以是任何,但我用一些FontAwesome图标和旋转的方法在那里:
<div style="text-align:center" ng-class="{ 'loading': !loading }"> <br /> <h1><i class="fa fa-refresh fa-spin"></i> Loading data</h1> </div>
在你想要隐藏的元素上,你只需写下这个:
<something ng-class="{ 'loading': loading }" class="loading"></something>
在函数中我只是设置这个负载。
(function (angular) { function MainController($scope) { $scope.loading = true
我正在使用SignalR,所以在hubProxy.client.allLocks函数(当它完成通过锁),我直接把
$scope.loading = false $scope.$apply();
当页面加载时,这也隐藏了{{someField}},因为我正在加载加载类,AngularJS在之后删除它。