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

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

错误:$digest已在进行中

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


当前回答

我已经能够通过在我知道$digest函数将运行的地方调用$eval而不是$apply来解决这个问题。

根据文档,$apply基本上做到了这一点:

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

在我的例子中,ng单击会更改范围内的变量,而该变量上的$watch会更改必须应用$apply的其他变量。最后一步导致错误“摘要已在进行中”。

通过在watch表达式中将$apply替换为$eval,作用域变量将按预期更新。

因此,如果因为Angular中的一些其他变化,digest将以任何方式运行,那么您需要做的就是$eval‘ing。

其他回答

我已经能够通过在我知道$digest函数将运行的地方调用$eval而不是$apply来解决这个问题。

根据文档,$apply基本上做到了这一点:

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

在我的例子中,ng单击会更改范围内的变量,而该变量上的$watch会更改必须应用$apply的其他变量。最后一步导致错误“摘要已在进行中”。

通过在watch表达式中将$apply替换为$eval,作用域变量将按预期更新。

因此,如果因为Angular中的一些其他变化,digest将以任何方式运行,那么您需要做的就是$eval‘ing。

这是我的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来防止错误。

$timeout(function () {
    var scope = angular.element($("#myController")).scope();
    scope.myMethod(); 
    scope.$scope();
}, 1);

安全$apply的最短形式是:

$timeout(angular.noop)

尝试使用

$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文件