首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何知道范围更改何时已传播到视图?

如何知道范围更改何时已传播到视图?
EN

Stack Overflow用户
提问于 2015-03-08 16:26:08
回答 3查看 332关注 0票数 4

如果在角模型中执行变量赋值:

代码语言:javascript
复制
$scope.foo = 1;

我如何知道什么时候视图已经根据我刚刚在模型上设置的新值完全更新了呢?

(我问这个问题的原因是,在实践中,我向$scope.foo提供了一个相当大的数据结构,它需要几秒钟的角度来更新屏幕上的视图,即使是在一台强大的桌面机器上,所以,我正在寻找方法来知道何时我可以安全地从屏幕上删除一个加载动画)

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-03-08 18:16:58

这个问题的“真正”答案是:

“你不能”

当您以角度对模型进行更改时,您完全不知道需要多少$digest()调用才能完全实现DOM。

假设您有一个非常大的数组foos -它‘有一百万个元素。

在标记中,可以执行以下操作:

代码语言:javascript
复制
<div ng-repeat="foo in foos"><div ng-if="foo.somecondition">I'm a visible foo!></div></div>

在控制器中,设置foos:

代码语言:javascript
复制
.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数组,为了保持浏览器响应性,我会这样做:

代码语言:javascript
复制
.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仍然在加载,并且所有这些都将工作。我很快就把这些放在一起,作为一个例子来说明这一点--我相信有一些方法可以使它更加优雅,甚至可能会有一些语法错误。

如果我是“真实地”这样做的话,我可能会将这个功能封装到服务本身中。

票数 1
EN

Stack Overflow用户

发布于 2015-03-08 16:48:02

此答案假定出现延迟是因为摘要周期正在运行。如果范围更新正在等待异步操作,那么@New中的答案是更正确的方法。

最好的方法可能是使用$timeout服务。使用它,您可以注册一个函数,以便在当前摘要周期完成后运行。您将希望为延迟参数指定零,为invokeApply参数指定true (或default),因为我假设您的加载动画是使用ngShow指令或其他什么来显示/隐藏的。

代码语言:javascript
复制
$timeout(function() { $scope.showLoadingAnimation = false; }, 0);

或者,您可以在作用域上使用无文档的$$postDigest方法。此方法将在下一个摘要周期完成后运行一次函数。请注意,此函数不会在$scope.$apply上下文中运行,因此,如果希望AngularJS注意到范围更改,则需要自己调用$apply

代码语言:javascript
复制
$scope.$$postDigest(function() { 
  $scope.$apply(function() { $scope.showLoadingAnimation = false; });
});

(此处插入关于使用无文档函数的标准免责声明。)

票数 1
EN

Stack Overflow用户

发布于 2015-03-08 16:47:21

如果以异步方式获取数据,则删除处理程序中的动画:

代码语言:javascript
复制
$scope.isLoading = true;
somSvc.loadData().then(function(data){
  $scope.foo = data;
  $scope.isLoading = false;
});

一旦将数据分配给范围变量,更改将在下一个摘要周期中进行。

这个分配需要一些时间,所以如果您需要事先更改视图中的某些内容,则需要延迟将数据分配给$scope.foo,这样角度就有机会更改视图。

您不能在分配数据之前就这么做,因为在您更改的同一个摘要周期中分配数据时,角将忙于呈现DOM:

代码语言:javascript
复制
$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

编辑:如果您只有一个“加载”指示符,那么上面的示例可以简化为:

代码语言:javascript
复制
$scope.isLoading = true;
somSvc.loadData().then(function(data){
  $scope.foo = data;
  $timeout(function(){
    $scope.isLoading = false;
  }, 0);
});
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28928853

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档