Angularjs 载入屏幕上的 Ajax 请求

使用 Angularjs,我需要显示一个加载屏幕(一个简单的微调器) ,直到 Ajax 请求完成。请使用代码片段提出任何建议。

181360 次浏览

这里有一个例子,它使用简单的带 bool 的 ng-show 方法。

超文本标示语言

<div ng-show="loading" class="loading"><img src="...">LOADING...</div>
<div ng-repeat="car in cars">
<li>\{\{car.name}}</li>
</div>
<button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>

ANGULARJS

  $scope.clickMe = function() {
$scope.loading = true;
$http.get('test.json')
.success(function(data) {
$scope.cars = data[0].cars;
$scope.loading = false;
});
}

当然,您可以将装载框 html 代码移动到指令中,然后在 $scope.load 上使用 $watch。在这种情况下:

HTML :

<loading></loading>

ANGULARJS 指令 :

  .directive('loading', function () {
return {
restrict: 'E',
replace:true,
template: '<div class="loading"><img src="..."/>LOADING...</div>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {
if (val)
$(element).show();
else
$(element).hide();
});
}
}
})

PLUNK: http://plnkr.co/edit/AI1z21?p=preview

此外,还有一个很好的演示,展示了如何在您的项目中使用 Angularjs动画。

这个链接是这里的一个 href = “ http://year ofmoo-articles.github.io/angularjs-Animation-article/app/#/ng-repeat”rel = “ nofollow noReferrer”> (见左上角)

这是一个开放源码。这里是到 下载的链接

这里是 教程的链接;

My point is, go ahead and download the source files and then see how they have implemented the spinner. They might have used a little better aproach. So, checkout this project.

与其设置一个作用域变量来指示数据加载状态,不如让一个指令为您做所有事情:

angular.module('directive.loading', [])


.directive('loading',   ['$http' ,function ($http)
{
return {
restrict: 'A',
link: function (scope, elm, attrs)
{
scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};


scope.$watch(scope.isLoading, function (v)
{
if(v){
elm.show();
}else{
elm.hide();
}
});
}
};


}]);

有了这个指令,你所需要做的就是给任何加载动画元素一个“ load”属性:

<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>

You can have multiple loading spinners on the page. where and how to layout those spinners is up to you and directive will simply turn it on/off for you automatically.

做到这一点的最佳方法是使用 响应拦截器响应拦截器响应拦截器和自定义 指令。采用 $rootScope. $Broadcasting$rootScope. $on方法,利用 酒吧/潜艇机制可以进一步改进工艺。

As the whole process is documented in a well written 博客文章, I'm not going to repeat that here again. Please refer to that article to come up with your needed implementation.

我用的是 进步

一旦在 HTML 中包含了 script/css 文件,就将“ ngProgress”添加到依赖项中。一旦您这样做了,您就可以设置类似这样的东西,它将在检测到路由更改时触发。

angular.module('app').run(function($rootScope, ngProgress) {
$rootScope.$on('$routeChangeStart', function(ev,data) {
ngProgress.start();
});
$rootScope.$on('$routeChangeSuccess', function(ev,data) {
ngProgress.complete();
});
});

对于 AJAX 请求,您可以这样做:

$scope.getLatest = function () {
ngProgress.start();


$http.get('/latest-goodies')
.success(function(data,status) {
$scope.latest = data;
ngProgress.complete();
})
.error(function(data,status) {
ngProgress.complete();
});
};

只要记住在这样做之前将‘ ngProgress’添加到控制器依赖项。如果您正在执行多个 AJAX 请求,那么在主应用程序范围内使用一个增量变量来跟踪 AJAX 请求何时完成,然后再调用‘ ngProgress. complete () ;’。

使用 PendingRequest 是不正确的,因为正如在 Angular 文档中提到的,此属性主要用于调试目的。

我建议使用拦截器来了解是否存在任何活动的异步调用。

module.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($q, $rootScope) {
if ($rootScope.activeCalls == undefined) {
$rootScope.activeCalls = 0;
}


return {
request: function (config) {
$rootScope.activeCalls += 1;
return config;
},
requestError: function (rejection) {
$rootScope.activeCalls -= 1;
return rejection;
},
response: function (response) {
$rootScope.activeCalls -= 1;
return response;
},
responseError: function (rejection) {
$rootScope.activeCalls -= 1;
return rejection;
}
};
});
}]);

and then check whether activeCalls is zero or not in the directive through a $watch.

module.directive('loadingSpinner', function ($http) {
return {
restrict: 'A',
replace: true,
template: '<div class="loader unixloader" data-initialize="loader" data-delay="500"></div>',
link: function (scope, element, attrs) {


scope.$watch('activeCalls', function (newVal, oldVal) {
if (newVal == 0) {
$(element).hide();
}
else {
$(element).show();
}
});
}
};
});

如果您正在使用 Restangle (这是令人敬畏的) ,您可以在 API 调用期间创建一个动画。这是我的解决办法。添加响应拦截器和发送 rootscope 广播的请求拦截器。然后创建一个监听响应和请求的指令:

         angular.module('mean.system')
.factory('myRestangular',['Restangular','$rootScope', function(Restangular,$rootScope) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://localhost:3000/api');
RestangularConfigurer.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
var extractedData;
// .. to look for getList operations
if (operation === 'getList') {
// .. and handle the data and meta data
extractedData = data.data;
extractedData.meta = data.meta;
} else {
extractedData = data.data;
}
$rootScope.$broadcast('apiResponse');
return extractedData;
});
RestangularConfigurer.setRequestInterceptor(function (elem, operation) {
if (operation === 'remove') {
return null;
}
return (elem && angular.isObject(elem.data)) ? elem : {data: elem};
});
RestangularConfigurer.setRestangularFields({
id: '_id'
});
RestangularConfigurer.addRequestInterceptor(function(element, operation, what, url) {
$rootScope.$broadcast('apiRequest');
return element;
});
});
}]);

Here is the directive:

        angular.module('mean.system')
.directive('smartLoadingIndicator', function($rootScope) {
return {
restrict: 'AE',
template: '<div ng-show="isAPICalling"><p><i class="fa fa-gear fa-4x fa-spin"></i>&nbsp;Loading</p></div>',
replace: true,
link: function(scope, elem, attrs) {
scope.isAPICalling = false;


$rootScope.$on('apiRequest', function() {
scope.isAPICalling = true;
});
$rootScope.$on('apiResponse', function() {
scope.isAPICalling = false;
});
}
};
})
;

Include this in your "app.config":

 $httpProvider.interceptors.push('myHttpInterceptor');

And add this code:

app.factory('myHttpInterceptor', function ($q, $window,$rootScope) {
$rootScope.ActiveAjaxConectionsWithouthNotifications = 0;
var checker = function(parameters,status){
//YOU CAN USE parameters.url TO IGNORE SOME URL
if(status == "request"){
$rootScope.ActiveAjaxConectionsWithouthNotifications+=1;
$('#loading_view').show();
}
if(status == "response"){
$rootScope.ActiveAjaxConectionsWithouthNotifications-=1;


}
if($rootScope.ActiveAjaxConectionsWithouthNotifications<=0){
$rootScope.ActiveAjaxConectionsWithouthNotifications=0;
$('#loading_view').hide();


}




};
return {
'request': function(config) {
checker(config,"request");
return config;
},
'requestError': function(rejection) {
checker(rejection.config,"request");
return $q.reject(rejection);
},
'response': function(response) {
checker(response.config,"response");
return response;
},
'responseError': function(rejection) {
checker(rejection.config,"response");
return $q.reject(rejection);
}
};
});

关于这个答案

Https://stackoverflow.com/a/17144634/4146239

对我来说是最好的解决方案,但是有一种方法可以避免使用 jQuery。

.directive('loading', function () {
return {
restrict: 'E',
replace:true,
template: '<div class="loading"><img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" width="20" height="20" />LOADING...</div>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {
if (val)
scope.loadingStatus = 'true';
else
scope.loadingStatus = 'false';
});
}
}
})


.controller('myController', function($scope, $http) {
$scope.cars = [];
      

$scope.clickMe = function() {
scope.loadingStatus = 'true'
$http.get('test.json')
.success(function(data) {
$scope.cars = data[0].cars;
$scope.loadingStatus = 'false';
});
}
      

});
<body ng-app="myApp" ng-controller="myController" ng-init="loadingStatus='true'">
<loading ng-show="loadingStatus" ></loading>
  

<div ng-repeat="car in cars">
<li>\{\{car.name}}</li>
</div>
<button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>
  

</body>

需要用 $scope.loadingStatus = ‘ true’和 $scope.loadingStatus = ‘ false’替换 $(element) . show () ; 和(element) . show () ;

然后,您需要使用这个变量来设置元素的 ng-show 属性。

打字和角度实现

指令

((): void=> {
"use strict";
angular.module("app").directive("busyindicator", busyIndicator);
function busyIndicator($http:ng.IHttpService): ng.IDirective {
var directive = <ng.IDirective>{
restrict: "A",
link(scope: Scope.IBusyIndicatorScope) {
scope.anyRequestInProgress = () => ($http.pendingRequests.length > 0);
scope.$watch(scope.anyRequestInProgress, x => {
if (x) {
scope.canShow = true;
} else {
scope.canShow = false;
}
});
}
};
return directive;
}
})();

范围

   module App.Scope {
export interface IBusyIndicatorScope extends angular.IScope {
anyRequestInProgress: any;
canShow: boolean;
}
}

模板

<div id="activityspinner" ng-show="canShow" class="show" data-busyindicator>
</div>


CSS
#activityspinner
{
display : none;
}
#activityspinner.show {
display : block;
position : fixed;
z-index: 100;
background-image : url('data:image/gif;base64,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')
-ms-opacity : 0.4;
opacity : 0.4;
background-repeat : no-repeat;
background-position : center;
left : 0;
bottom : 0;
right : 0;
top : 0;
}

使用 有棱角的忙碌:

将 cgBusy 添加到应用程序/模块:

angular.module('your_app', ['cgBusy']);

把你的承诺加到 scope:

function MyCtrl($http, User) {
//using $http
this.isBusy = $http.get('...');
//if you have a User class based on $resource
this.isBusy = User.$save();
}

In your html template:

<div cg-busy="$ctrl.isBusy"></div>

我在@DavidLin 的回答基础上稍微简化了一下——指令中的 removing any dependency on jQuery。当我在生产应用程序中使用它时,我可以确认它的工作原理

function AjaxLoadingOverlay($http) {


return {
restrict: 'A',
link: function ($scope, $element, $attributes) {


$scope.loadingOverlay = false;


$scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};


$scope.$watch($scope.isLoading, function (isLoading) {
$scope.loadingOverlay = isLoading;
});
}
};
}

我使用 ng-show代替 jQuery 调用来隐藏/显示 <div>

下面是我放在 <body>标签下面的 <div>:

<div ajax-loading-overlay class="loading-overlay" ng-show="loadingOverlay">
<img src="Resources/Images/LoadingAnimation.gif" />
</div>

下面是 CSS,它在调用 $http 时为块 UI 提供覆盖:

.loading-overlay {
position: fixed;
z-index: 999;
height: 2em;
width: 2em;
overflow: show;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
}


.loading-overlay:before {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.3);
}


/* :not(:required) hides these rules from IE9 and below */
.loading-overlay:not(:required) {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}

CSS 的荣誉归于@Steve Seeger’s-his post: https://stackoverflow.com/a/35470281/335545

这里是一个简单的拦截器示例,我在 Ajax 启动时将鼠标设置为等待,在 Ajax 结束时将其设置为自动。

$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;
}
};
});

Code has to be added in application config app.config. I showed how to change mouse on loading state but in there it is possible to show/hide any loader content, or add, remove some css classes which are showing the loader.

拦截器将在每个 ajax 调用上运行,因此不需要在每个 http 调用上创建特殊的布尔变量($scope.load = true/false 等)。

拦截器是使用内置的角 JqLite https://docs.angularjs.org/api/ng/function/angular.element,所以没有 Jquery 需要。

使用 show 和 size 属性创建一个 Directive (也可以添加更多属性)

    app.directive('loader',function(){
return {
restrict:'EA',
scope:{
show : '@',
size : '@'
},
template : '<div class="loader-container"><div class="loader" ng-if="show" ng-class="size"></div></div>'
}
})

以及在 html 中使用

 <loader show="\{\{loader1}}" size="sm"></loader>

In the 表演 variable pass 没错 when any promise is running and make that 假的 when request is completed. 活动演示-在 JsFiddle 中的 Angular Loader 指令示例演示 > 在 JsFiddle 中的 Angular Loader 指令示例演示

您可以添加一个条件,然后通过 rootScope 修改它

angular.module('directive.loading', [])
.directive('loading',   ['$http', '$rootScope',function ($http, $rootScope)
{
return {
restrict: 'A',
link: function (scope, elm, attrs)
{
scope.isNoLoadingForced = false;
scope.isLoading = function () {
return $http.pendingRequests.length > 0 && scope.isNoLoadingForced;
};


$rootScope.$on('stopLoader', function(){
scope.isNoLoadingForced = true;
})


scope.$watch(scope.isLoading, function (v)
{
if(v){
elm.show();
}else{
elm.hide();
}
});
}
};


}]);

This is definatly not the best solution but it would still works.