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

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

错误:$digest已在进行中

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


当前回答

在为我们创建一个可重用的$safeApply函数方面,yearmomo做得很好:

https://github.com/yearofmoo/AngularJS-Scope.SafeApply

用法:

//use by itself
$scope.$safeApply();

//tell it which scope to update
$scope.$safeApply($scope);
$scope.$safeApply($anotherScope);

//pass in an update function that gets called when the digest is going on...
$scope.$safeApply(function() {

});

//pass in both a scope and a function
$scope.$safeApply($anotherScope,function() {

});

//call it on the rootScope
$rootScope.$safeApply();
$rootScope.$safeApply($rootScope);
$rootScope.$safeApply($scope);
$rootScope.$safeApply($scope, fn);
$rootScope.$safeApply(fn);

其他回答

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

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

或者如果你有lodash:

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

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

我建议您使用自定义事件,而不是触发摘要循环。

我发现,广播自定义事件并为此事件注册侦听器是一个很好的解决方案,无论您是否处于摘要周期,都可以触发您希望发生的操作。

通过创建自定义事件,您的代码也会更高效,因为您只触发订阅所述事件的侦听器,而不会像调用作用域时那样触发绑定到作用域的所有监视$申请

$scope.$on('customEventName', function (optionalCustomEventArguments) {
   //TODO: Respond to event
});


$scope.$broadcast('customEventName', optionalCustomEventArguments);

使用$scope$$阶段||$scope$apply();相反

        let $timeoutPromise = null;
        $timeout.cancel($timeoutPromise);
        $timeoutPromise = $timeout(() => {
            $scope.$digest();
        }, 0, false);

以下是避免此错误和避免$apply的好解决方案

如果基于外部事件调用,则可以将其与反跳(0)结合使用。以上是我们正在使用的“反跳”,以及完整的代码示例

.factory('debounce', [
    '$timeout',
    function ($timeout) {

        return function (func, wait, apply) {
            // apply default is true for $timeout
            if (apply !== false) {
                apply = true;
            }

            var promise;
            return function () {
                var cntx = this,
                    args = arguments;
                $timeout.cancel(promise);
                promise = $timeout(function () {
                    return func.apply(cntx, args);
                }, wait, apply);
                return promise;
            };
        };
    }
])

以及代码本身,以侦听某些事件并仅在需要的$scope上调用$digest

        let $timeoutPromise = null;
        let $update = debounce(function () {
            $timeout.cancel($timeoutPromise);
            $timeoutPromise = $timeout(() => {
                $scope.$digest();
            }, 0, false);
        }, 0, false);

        let $unwatchModelChanges = $scope.$root.$on('updatePropertiesInspector', function () {
            $update();
        });


        $scope.$on('$destroy', () => {
            $timeout.cancel($update);
            $timeout.cancel($timeoutPromise);
            $unwatchModelChanges();
        });

如果你这样做,有时你仍然会出错(https://stackoverflow.com/a/12859093/801426).

试试看:

if(! $rootScope.$root.$$phase) {
...