在angularjs 1.2中,过滤ng-repeat
多行(> 2,000行)的操作可能会变得很慢(> 1秒)。
我知道我可以使用limitTo
,分页,自定义过滤器等来优化执行时间。但我仍然有兴趣知道在浏览器忙于运行长脚本时是否可以显示加载动画。
在有角度的情况下,我认为只要$digest
运行就可以调用,因为这似乎是占用大部分时间的主函数,可能会多次调用。
在related question中,没有给出有用的答案。任何帮助非常感谢!
答案 0 :(得分:20)
问题是只要Javascript正在执行,UI就没有机会更新。即使您在过滤之前呈现微调器,只要Angular忙,它就会显示为“冻结”。
解决此问题的一种方法是过滤块,如果有更多数据可用,请在小$timeout
之后再次过滤。超时使UI线程有机会运行并显示更改和动画。
证明这一原则的小提琴是here。
它不使用Angular的过滤器(它们是同步的)。相反,它使用以下函数过滤data
数组:
function filter() {
var i=0, filtered = [];
innerFilter();
function innerFilter() {
var counter;
for( counter=0; i < $scope.data.length && counter < 5; counter++, i++ ) {
/////////////////////////////////////////////////////////
// REAL FILTER LOGIC; BETTER SPLIT TO ANOTHER FUNCTION //
if( $scope.data[i].indexOf($scope.filter) >= 0 ) {
filtered.push($scope.data[i]);
}
/////////////////////////////////////////////////////////
}
if( i === $scope.data.length ) {
$scope.filteredData = filtered;
$scope.filtering = false;
}
else {
$timeout(innerFilter, 10);
}
}
}
当过滤器处于活动状态时,它需要2个支持变量:$scope.filtering
为true
,这样我们就有机会显示微调器并禁用输入; $scope.filteredData
会收到过滤器的结果。
有3个隐藏参数:
counter < 5
)的目的很小,以证明效果$timeout(innerFilter, 10)
)应该很小,但足以让UI线程有时间做出响应这只是概念的证明;我建议重构它(可能是指令)以供实际使用。
答案 1 :(得分:7)
以下是步骤:
ng-view
之外
fixed
定位。<div class="activity-box" ng-show="!!message">
<img src="img/load.png" width="40" height="40" />
<span>{{ message }}</span>
</div>
activity
指令:具有单个属性message
的简单指令。请注意上面模板中的ng-show
指令。 message
既可用于切换活动指示符,也可用于设置用户的信息文本。
app.directive('activity', [
function () {
return {
restrict: 'EA',
templateUrl: '/templates/activity.html',
replace: true,
scope: {
message: '@'
},
link: function (scope, element, attrs) {}
};
}
]);
<body ng-controller="appController">
<div ng-view id="content-view">...</div>
<div activity message="{{ activityMessage }}"></div>
</body>
请注意,activity
指令位于ng-view
之外。它将在您的单页面应用程序的每个部分提供。
app.controller('appController',
function ($scope, $timeout) {
// You would better place these two methods below, inside a
// service or factory; so you can inject that service anywhere
// within the app and toggle the activity indicator on/off where needed
$scope.showActivity = function (msg) {
$timeout(function () {
$scope.activityMessage = msg;
});
};
$scope.hideActivity = function () {
$timeout(function () {
$scope.activityMessage = '';
}, 1000); // message will be visible at least 1 sec
};
// So here is how we do it:
// "Before" the process, we set the message and the activity indicator is shown
$scope.showActivity('Loading items...');
var i;
for (i = 0; i < 10000; i += 1) {
// here goes some heavy process
}
// "After" the process completes, we hide the activity indicator.
$scope.hideActivity();
});
当然,你也可以在其他地方使用它。例如您可以在承诺结算时致电$scope.hideActivity();
。或者在request
的{{1}}和response
上切换活动也是一个好主意。
httpInterceptor
希望这有帮助。
答案 2 :(得分:5)
使用spin.js,网站http://fgnass.github.com/spin.js/显示非常简单的步骤。 加载动画在CSS中,与UI线程分开,因此可以顺利加载。
答案 3 :(得分:2)
您可以做的是检测ngRepeat
的结尾为this post所说的。
我会在控制器中做类似的事情:
$scope.READY = false;
在指令中,正如上面的帖子所说,我会做类似的事情:
if (scope.$last) {
$scope.READY = true;
}
你可以拥有一个基于CSS的加载器/微调器
<div class="loader" ng-show="!READY">
</div>
当然,你也可以拥有基于CSS的动画,这些动画独立于js执行。
答案 4 :(得分:1)
您可以使用WebWorkers在另一个线程中运行过滤器,因此您的angularjs页面不会阻止。
如果您不使用网络工作者,浏览器可能会获得javascript执行超时并完全停止您的角度应用程序,即使您没有得到任何超时,您的应用程序也会冻结,直到计算结束。
更新23.04.14
答案 5 :(得分:1)
以下是一个工作示例: -
angular
.module("APP", [])
.controller("myCtrl", function ($scope, $timeout) {
var mc = this
mc.loading = true
mc.listRendered = []
mc.listByCategory = []
mc.categories = ["law", "science", "chemistry", "physics"]
mc.filterByCategory = function (category) {
mc.loading = true
// This timeout will start on the next event loop so
// filterByCategory function will exit just triggering
// the show of Loading... status
// When the function inside timeout is called, it will
// filter and set the model values and when finished call
// an inbuilt $digest at the end.
$timeout(function () {
mc.listByCategory = mc.listFetched.filter(function (ele) {
return ele.category === category
})
mc.listRendered = mc.listByCategory
$scope.$emit("loaded")
}, 50)
}
// This timeout is replicating the data fetch from a server
$timeout(function () {
mc.listFetched = makeList()
mc.listRendered = mc.listFetched
mc.loading = false
}, 50)
$scope.$on("loaded", function () { mc.loading = false })
})
function makeList() {
var list = [
{name: "book1", category: "law"},
{name: "book2", category: "science"},
{name: "book1", category: "chemistry"},
{name: "book1", category: "physics"}
]
var bigList = []
for (var i = 0; i <= 5000; i++) {
bigList = bigList.concat(list)
}
return bigList
}
button {
display: inline-block;
}
<html>
<head>
<title>This is an Angular Js Filter Workaround!!</title>
</head>
<body ng-app="APP">
<div ng-controller="myCtrl as mc">
<div class = "buttons">
<label>Select Category:- </label>
<button ng-repeat="category in mc.categories" ng-click="mc.filterByCategory(category)">{{category}}</button>
</div>
<h1 ng-if="mc.loading">Loading...</h1>
<ul ng-if="!mc.loading">
<li ng-repeat="ele in mc.listRendered track by $index">{{ele.name}} - {{ele.category}}</li>
</ul>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
</body>
<html>
答案 6 :(得分:0)
在这种情况下可以使用Promise / deferred,您可以调用notify来查看代码的进度,来自angular.js的文档:https://code.angularjs.org/1.2.16/docs/api/ng/service/ $ q 这是一个关于使用ng-Promise的重JS处理的教程:http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/,希望它有用。
// app工厂用于保存数据模型
app.factory('postFactory',function($ http,$ q,$ timeout){
var factory = {
posts : false,
getPosts : function(){
var deferred = $q.defer();
//avoiding the http.get request each time
//we call the getPosts function
if (factory.posts !== false){
deferred.resolve(factory.posts);
}else{
$http.get('posts.json')
.success(function(data, status){
factory.posts = data
//to show the loading !
$timeout(function(){
deferred.resolve(factory.posts)
}, 2000);
})
.error(function(data, status){
deferred.error('Cant get the posts !')
})
};
return deferred.promise;
},
getPost : function(id){
//promise
var deferred = $q.defer();
var post = {};
var posts = factory.getPosts().then(function(posts){
post = factory.posts[id];
//send the post if promise kept
deferred.resolve(post);
}, function(msg){
deferred.reject(msg);
})
return deferred.promise;
},
};
return factory;
});
答案 7 :(得分:0)
您可以使用此网址中的代码: http://www.directiv.es/angular-loading-bar
你也可以找到一个工作演示。
这是代码:
<!DOCTYPE html>
<html ng-app="APP">
<head>
<meta charset="UTF-8">
<title>angular-loading-bar example</title>
<link rel="stylesheet" type="text/css" href="/application/html/js/chieffancypants/angular-loading-bar/loading-bar.min.css"/>
<style>
body{
padding:25px;
}
</style>
</head>
<body ng-controller="ExampleController">
<button id="reloader" ng-click="getUsers()">Reload</button>
<ul ng-repeat="person in data">
<li>{{person.lname}}, {{person.fname}}</li>
</ul>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular-animate.min.js"></script>
<script src="/application/html/js/chieffancypants/angular-loading-bar/loading-bar.min.js"></script>
<script>
angular.module('APP', ['chieffancypants.loadingBar', 'ngAnimate']).
controller("ExampleController",['$scope','$http',function($scope,$http){
$scope.getUsers = function(){
$scope.data=[];
var url = "http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}&delay=3&callback=JSON_CALLBACK"
$http.jsonp(url).success(function(data){
$scope.data=data;
})
}
$scope.getUsers()
}])
</script>
</body>
</html>
我该如何使用它?
通过npm或bower
安装$ npm install angular-loading-bar
$ bower install angular-loading-bar
要使用,只需将其作为依赖项添加到您的应用中即可完成!
angular.module('myApp', ['angular-loading-bar'])