我发现自从在angular中构建应用程序以来,我越来越需要手动将页面更新到我的范围。

我知道的唯一方法是从控制器和指令的范围调用$apply()。这样做的问题是,它不断向控制台抛出一个错误,错误如下:

错误:$digest已在进行中

有人知道如何避免这种错误或以不同的方式实现相同的事情吗?


当前回答

首先,不要这样解决

if ( ! $scope.$$phase) { 
  $scope.$apply(); 
}

它没有意义,因为$phase只是$digest循环的布尔标志,所以$apply()有时无法运行。记住这是一种糟糕的做法。

而是使用$timeout

    $timeout(function(){ 
  // Any code in here will automatically have an $scope.apply() run afterwards 
$scope.myvar = newValue; 
  // And it just works! 
});

如果使用下划线或lodash,可以使用defer():

_.defer(function(){ 
  $scope.$apply(); 
});

其他回答

保持此过程干燥的简便小助手方法:

function safeApply(scope, fn) {
    (scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}

尝试使用

$scope.applyAsync(function() {
    // your code
});

而不是

if(!$scope.$$phase) {
  //$digest or $apply
}

$applysync将$apply的调用安排为稍后进行。这可用于对需要在同一摘要中计算的多个表达式进行排队。

注意:在$digest中,如果当前作用域是$rootScope,则$applySync()将仅刷新。这意味着,如果在子作用域上调用$digest,它将不会隐式刷新$applysync()队列。

Exmaple公司:

  $scope.$applyAsync(function () {
                if (!authService.authenticated) {
                    return;
                }

                if (vm.file !== null) {
                    loadService.setState(SignWizardStates.SIGN);
                } else {
                    loadService.setState(SignWizardStates.UPLOAD_FILE);
                }
            });

参考文献:

1.范围.$applySync()与范围$AngularJS 1.3中的evalAsync()

AngularJs文件

这是我的utils服务:

angular.module('myApp', []).service('Utils', function Utils($timeout) {
    var Super = this;

    this.doWhenReady = function(scope, callback, args) {
        if(!scope.$$phase) {
            if (args instanceof Array)
                callback.apply(scope, Array.prototype.slice.call(args))
            else
                callback();
        }
        else {
            $timeout(function() {
                Super.doWhenReady(scope, callback, args);
            }, 250);
        }
    };
});

这是它的用法示例:

angular.module('myApp').controller('MyCtrl', function ($scope, Utils) {
    $scope.foo = function() {
        // some code here . . .
    };

    Utils.doWhenReady($scope, $scope.foo);

    $scope.fooWithParams = function(p1, p2) {
        // some code here . . .
    };

    Utils.doWhenReady($scope, $scope.fooWithParams, ['value1', 'value2']);
};

摘要周期是一个同步调用。在完成之前,它不会将控制权交给浏览器的事件循环。有几种方法可以解决这个问题。处理此问题的最简单方法是使用内置的$timeout,第二种方法是如果您使用下划线或lodash(您应该使用),请调用以下命令:

$timeout(function(){
    //any code in here will automatically have an apply run afterwards
});

或者如果你有lodash:

_.defer(function(){$scope.$apply();});

我们尝试了几种变通方法,我们讨厌将$rootScope注入所有控制器、指令,甚至一些工厂。因此,到目前为止,$timeout和_.defer一直是我们的最爱。这些方法成功地告诉angular等待下一个动画循环,这将保证当前范围$申请已结束。

当您收到此错误时,基本上意味着它已经在更新视图的过程中。您真的不需要在控制器中调用$apply()。如果您的视图没有按预期进行更新,并且在调用$apply()后出现此错误,那么很可能意味着您没有正确更新模型。如果你发布一些细节,我们可以找出核心问题。