En utilisant Angularjs, je dois afficher un écran de chargement (un simple disque tournant) jusqu'à ce que la demande ajax soit terminée. Veuillez suggérer toute idée avec un extrait de code.
Au lieu de configurer une variable de portée pour indiquer l'état de chargement des données, il est préférable qu'une directive fasse tout pour vous:
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();
}
});
}
};
}]);
Avec cette directive, tout ce que vous avez à faire est de donner à chaque élément d’animation de chargement un attribut "chargement":
<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>
Vous pouvez avoir plusieurs fileurs de chargement sur la page. Où et comment mettre en place ces fileuses et la directive l’activera/la désactivera automatiquement pour vous.
Voici un exemple. Il utilise la méthode simple ng-show avec un bool.
HTML
<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;
});
}
Bien sûr, vous pouvez déplacer le code html de la boîte de chargement dans une directive, puis utiliser $ watch sur $ scope.loading. Dans quel cas:
HTML:
<loading></loading>
DIRECTIVE 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();
});
}
}
})
J'utilise ngProgress pour cela.
Ajoutez 'ngProgress' à vos dépendances une fois que vous avez inclus les fichiers script/css dans votre code HTML. Une fois que vous avez fait cela, vous pouvez configurer quelque chose comme ceci, qui se déclenchera quand un changement d’itinéraire sera détecté.
angular.module('app').run(function($rootScope, ngProgress) {
$rootScope.$on('$routeChangeStart', function(ev,data) {
ngProgress.start();
});
$rootScope.$on('$routeChangeSuccess', function(ev,data) {
ngProgress.complete();
});
});
Pour AJAX demandes vous pouvez faire quelque chose comme ceci:
$scope.getLatest = function () {
ngProgress.start();
$http.get('/latest-goodies')
.success(function(data,status) {
$scope.latest = data;
ngProgress.complete();
})
.error(function(data,status) {
ngProgress.complete();
});
};
Rappelez-vous simplement d'ajouter 'ngProgress' aux dépendances des contrôleurs avant de le faire. Et si vous effectuez plusieurs requêtes AJAX, utilisez une variable incrémentielle dans la portée de l'application principale pour garder trace lorsque vos requêtes AJAX sont terminées avant d'appeler 'ngProgress.complete ();'.
l'utilisation de pendingRequests n'est pas correcte car, comme indiqué dans la documentation Angular, cette propriété est principalement destinée à être utilisée à des fins de débogage.
Ce que je recommande est d'utiliser un intercepteur pour savoir s'il existe un appel Async actif.
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;
}
};
});
}]);
puis vérifiez si activeCalls est égal à zéro ou non dans la directive via une surveillance $.
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();
}
});
}
};
});
La meilleure façon de procéder consiste à utiliser les intercepteurs response avec la directive personnalisée . Et le processus peut encore être amélioré en utilisant le mécanisme pub/sub en utilisant les méthodes $ rootScope. $ Broadcast & $ rootScope. $ On.
Comme l'ensemble du processus est documenté dans un article de blog bien écrit, _, je ne vais pas le répéter ici. Veuillez vous référer à cet article pour trouver votre mise en œuvre nécessaire.
En référence à cette réponse
https://stackoverflow.com/a/17144634/4146239
Pour moi, la meilleure solution, mais il existe un moyen d'éviter d'utiliser 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>
Vous devez remplacer $ (element) .show (); et (élément) .show (); avec $ scope.loadingStatus = 'true'; et $ scope.loadingStatus = 'false';
Ensuite, vous devez utiliser cette variable pour définir l'attribut ng-show de l'élément.
TypeScript et mise en œuvre angulaire
directive
((): 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;
}
})();
Portée
module App.Scope {
export interface IBusyIndicatorScope extends angular.IScope {
anyRequestInProgress: any;
canShow: boolean;
}
}
Modèle
<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;
}
Il existe également une démo de Nice qui montre comment utiliser l'animation Angularjs
dans votre projet.
le lien est ici
http://yearofmoo-articles.github.io/angularjs-animation-article/app/#/ng-repeat(voir le coin supérieur gauche).
C'est une source ouverte. voici le lien pour télécharger
https://github.com/yearofmoo-articles/AngularJS-Animation-Article
Et voici le lien pour le tutoriel;
http://www.yearofmoo.com/2013/04/animation-in-angularjs.html
Mon problème est de télécharger les fichiers source et de voir ensuite comment ils ont implémenté le spinner. Ils auraient peut-être utilisé un peu mieux après. Alors, vérifiez ce projet.
Incluez ceci dans votre "app.config":
$httpProvider.interceptors.Push('myHttpInterceptor');
Et ajoutez ce 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);
}
};
});
Si vous utilisez Restangular (ce qui est génial), vous pouvez créer une animation pendant les appels d'API. Voici ma solution. Ajoutez un intercepteur de réponse et un intercepteur de requête qui envoie une diffusion de rootscope. Créez ensuite une directive pour écouter cette réponse et cette requête:
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;
});
});
}]);
Voici la 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> 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;
});
}
};
})
;
Utilisez angular-busy :
Ajoutez cgBusy à votre application/module:
angular.module('your_app', ['cgBusy']);
Ajoutez votre promesse à scope
:
function MyCtrl($http, User) {
//using $http
this.isBusy = $http.get('...');
//if you have a User class based on $resource
this.isBusy = User.$save();
}
Dans votre modèle html:
<div cg-busy="$ctrl.isBusy"></div>
Ici, par exemple, un intercepteur simple, je mets la souris sur wait quand ajax démarre et le règle sur auto quand ajax se termine.
$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;
}
};
});
Le code doit être ajouté dans l'application config app.config
. J'ai montré comment changer la souris lors du chargement, mais il est possible d'afficher ou de masquer le contenu du chargeur, ou d'ajouter, de supprimer certaines classes CSS qui affichent le chargeur.
Interceptor s'exécutera à chaque appel ajax. Il n'est donc pas nécessaire de créer des variables booléennes spéciales ($ scope.loading = true/false, etc.) pour chaque appel http.
Interceptor utilise une construction angulaire jqLitehttps://docs.angularjs.org/api/ng/function/angular.element donc aucune Jquery n’est nécessaire.
Créez une directive avec les attributs show et size (vous pouvez en ajouter d'autres aussi)
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>'
}
})
et en HTML utiliser comme
<loader show="{{loader1}}" size="sm"></loader>
Dans la variable show, passez true lorsqu'une promesse est en cours d'exécution et exécutez-la false lorsque la demande est complétée . Démo active - Exemple de directive Ango Loader en démonstration JsFiddle
Vous pouvez ajouter une condition, puis la modifier via le rootscope. Avant votre demande ajax, vous appelez simplement $ rootScope. $ Emit ('stopLoader');
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();
}
});
}
};
}]);
Ce n'est certainement pas la meilleure solution mais cela fonctionnerait quand même.
Je me suis un peu inspiré de la réponse de @ DavidLin pour la simplifier - en supprimant toute dépendance à jQuery dans la directive. Je peux confirmer que cela fonctionne car je l'utilise dans une application de production
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;
});
}
};
}
J'utilise un ng-show
au lieu d'un appel jQuery pour masquer/afficher le <div>
.
Voici le <div>
que j'ai placé juste en dessous de la balise d'ouverture <body>
:
<div ajax-loading-overlay class="loading-overlay" ng-show="loadingOverlay">
<img src="Resources/Images/LoadingAnimation.gif" />
</div>
Et voici le CSS qui fournit la superposition pour bloquer l’interface utilisateur pendant un appel $ http:
.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;
}
Le crédit CSS va à @Steve Seeger - son post: https://stackoverflow.com/a/35470281/335545