Angularjs を使用して、ajax リクエストが完了するまでロード画面 (単純なスピナー) を表示する必要があります。コードスニペットでアイデアを提案してください。
15 に答える
データの読み込みステータスを示すスコープ変数を設定する代わりに、ディレクティブがすべてを実行するようにすることをお勧めします。
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();
}
});
}
};
}]);
このディレクティブを使用すると、読み込み中のアニメーション要素に「loading」属性を与えるだけで済みます。
<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>
ページに複数の読み込みスピナーを配置できます。これらのスピナーをどこにどのようにレイアウトするかはあなた次第であり、ディレクティブは自動的にオン/オフを切り替えるだけです。
これが例です。ブール値を使用した単純な ng-show メソッドを使用します。
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;
});
}
もちろん、ローディング ボックスの HTML コードをディレクティブに移動して、$scope.loading で $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();
});
}
}
})
これにはngProgressを使用します。
HTML にスクリプト/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 リクエストを実行している場合は、メイン アプリ スコープでインクリメンタル変数を使用して、「ngProgress.complete();」を呼び出す前に AJAX リクエストがいつ終了したかを追跡します。
これを行う最善の方法は、カスタムディレクティブと共に応答インターセプターを使用することです。$rootScope.$broadcastおよび$rootScope.$onメソッドを使用してpub/subメカニズムを使用すると、プロセスをさらに改善できます。
プロセス全体はよく書かれたブログ記事に記載されているので、ここでは繰り返しません。その記事を参照して、必要な実装を見つけてください。
この回答を参考に
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>
$(element).show(); を置き換える必要があります。および(要素).show(); $scope.loadingStatus = 'true'; および $scope.loadingStatus = 'false';
それよりも、この変数を使用して要素の ng-show 属性を設定する必要があります。
Restangular (素晴らしい) を使用している場合は、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;
});
});
}]);
ディレクティブは次のとおりです。
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;
});
}
};
})
;
これを「app.config」に含めます。
$httpProvider.interceptors.push('myHttpInterceptor');
そして、次のコードを追加します。
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);
}
};
});
angular-busyを使用します。
アプリ/モジュールに 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();
}
HTML テンプレートで:
<div cg-busy="$ctrl.isBusy"></div>
ここでの簡単なインターセプターの例では、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;
}
};
});
アプリケーション構成にコードを追加する必要がありますapp.config
。ロード状態でマウスを変更する方法を示しましたが、ローダーのコンテンツを表示/非表示にしたり、ローダーを表示しているいくつかの css クラスを追加、削除したりできます。
Interceptor はすべての ajax 呼び出しで実行されるため、すべての http 呼び出しで特別なブール変数 ( $scope.loading=true/false など) を作成する必要はありません。
インターセプターは組み込みの angular jqLite https://docs.angularjs.org/api/ng/function/angular.elementを使用しているため、Jquery は必要ありません。
show 属性と size 属性を使用してディレクティブを作成します (さらに追加することもできます)。
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>
show変数では、promise が実行されているときに true を渡し、リクエストが完了したときにfalseにします。アクティブなデモ - JsFiddle の Angular Loader ディレクティブのサンプル デモ