如果在角模型中执行变量赋值:
$scope.foo = 1;我如何知道什么时候视图已经根据我刚刚在模型上设置的新值完全更新了呢?
(我问这个问题的原因是,在实践中,我向$scope.foo提供了一个相当大的数据结构,它需要几秒钟的角度来更新屏幕上的视图,即使是在一台强大的桌面机器上,所以,我正在寻找方法来知道何时我可以安全地从屏幕上删除一个加载动画)
发布于 2015-03-08 18:16:58
这个问题的“真正”答案是:
“你不能”
当您以角度对模型进行更改时,您完全不知道需要多少$digest()调用才能完全实现DOM。
假设您有一个非常大的数组foos -它‘有一百万个元素。
在标记中,可以执行以下操作:
<div ng-repeat="foo in foos"><div ng-if="foo.somecondition">I'm a visible foo!></div></div>在控制器中,设置foos:
.controller('myController', function($scope, $fooService) {
$scope.foos = $fooService.get();
$timeout(function(){
//foo array done? Not quite!!!!
});
});(在本例中,get()不是异步的,它立即返回foos数组)。
在运行控制器构造函数之后,$digest()将(最终)以角方式运行。这个摘要周期将在DOM中生成100万行(这将花费很长的时间,并在修改DOM时锁定浏览器)。在摘要的末尾,如果在控制器中使用$timeout,则会触发$timeout函数,但DOM尚未完全构建。
如果在那一刻检查DOM,您会发现您有您的百万行,但是内部ng-if还没有被计算。角,在第一个消化周期结束时,会注意到需要另一个消化周期,并且它将直接进入运行下一个消化周期--在您的$timeout被评估之后。
当然,这是一个相当人为的例子。如果以异步方式加载数据,情况会变得更糟,等等。
我建议真正的问题是,foo数据太大了--如果不知道实现的细节,我就无法提供更多帮助来帮助您了解如何更好地管理问题。
关键是,在角度上,只关心模型的状态,而不是DOM的状态。
如果我要尝试显示一个百万foos数组,为了保持浏览器响应性,我会这样做:
.controller('myController', function($scope, $fooService) {
$scope.loading = true;
$scope.foos = [];
var length = $fooService.length; //1,000,000 foos
var i = 0;
var loadFoos = function() {
var count = 0;
while(count < 10) {
$scope.foos.push($fooService.get(i + count++));
if(i + count == length) {
$scope.loading = false; //we're done!
return;
}
}
i += count;
//note, using setTimeout vs. $timeout deliberately - I want each digest cycle to completely finish and return the control of the execution context to the browser.
setTimeout(function(){
$scope.$apply(function() {
loadFoos();
}, 0);
})
};
loadFoos(); //kick things off.
});我正在做的是一次加载foos 10,然后在过渡期间将控制返回到浏览器。现在您可以使用$scope.loading标志来指示foos仍然在加载,并且所有这些都将工作。我很快就把这些放在一起,作为一个例子来说明这一点--我相信有一些方法可以使它更加优雅,甚至可能会有一些语法错误。
如果我是“真实地”这样做的话,我可能会将这个功能封装到服务本身中。
发布于 2015-03-08 16:48:02
此答案假定出现延迟是因为摘要周期正在运行。如果范围更新正在等待异步操作,那么@New中的答案是更正确的方法。
最好的方法可能是使用$timeout服务。使用它,您可以注册一个函数,以便在当前摘要周期完成后运行。您将希望为延迟参数指定零,为invokeApply参数指定true (或default),因为我假设您的加载动画是使用ngShow指令或其他什么来显示/隐藏的。
$timeout(function() { $scope.showLoadingAnimation = false; }, 0);或者,您可以在作用域上使用无文档的$$postDigest方法。此方法将在下一个摘要周期完成后运行一次函数。请注意,此函数不会在$scope.$apply上下文中运行,因此,如果希望AngularJS注意到范围更改,则需要自己调用$apply。
$scope.$$postDigest(function() {
$scope.$apply(function() { $scope.showLoadingAnimation = false; });
});(此处插入关于使用无文档函数的标准免责声明。)
发布于 2015-03-08 16:47:21
如果以异步方式获取数据,则删除处理程序中的动画:
$scope.isLoading = true;
somSvc.loadData().then(function(data){
$scope.foo = data;
$scope.isLoading = false;
});一旦将数据分配给范围变量,更改将在下一个摘要周期中进行。
这个分配需要一些时间,所以如果您需要事先更改视图中的某些内容,则需要延迟将数据分配给$scope.foo,这样角度就有机会更改视图。
您不能在分配数据之前就这么做,因为在您更改的同一个摘要周期中分配数据时,角将忙于呈现DOM:
$scope.isLoading = true;
somSvc.loadData().then(function(data){
$scope.isLoading = false;
$scope.isRendering = true;
$timeout(function(){
$scope.foo = data;
$scope.isRendering = false;
}, 0);
});http://plnkr.co/edit/KslF9lED0tkT7bKJ81LF?p=preview
编辑:如果您只有一个“加载”指示符,那么上面的示例可以简化为:
$scope.isLoading = true;
somSvc.loadData().then(function(data){
$scope.foo = data;
$timeout(function(){
$scope.isLoading = false;
}, 0);
});https://stackoverflow.com/questions/28928853
复制相似问题