0

In my controller, I set a variable "data" to a default value. In my original project, I use CouchCorner to fetch data from a CouchDB and update data.

Within a derictive I watch the variable data and update the element accordingly. However, I ran in the problem of "$digest already in progress", which results that the derective is not updated correctly. I already found a lot of information on the $digest problem, but I couldn't find any answer which fits to my problem.

I created the following jsFiddle, which uses $timeout instead of an actual CouchDB request: http://jsfiddle.net/cwesM/4/

var App = angular.module('myApp', []);

function Ctrl($scope, $timeout) {
$scope.data="Initial";

$timeout(function(){
    $scope.data="Updated"}, 1)
}

App.directive('chart', function(){
    return{
        restrict: 'E',
        link: function(scope, elem, attrs){
            scope.$watch(attrs.ngModel, function(v){
                alert(v)
                elem.html("Data="+v);
            });
        }
   };
});

The script changes the initial value of "data" to "Updated" after 1ms. But the directive is not updated, although the $watch function is executed. Interestingly, if I remove the alert(), everything works as expected.

How I can avoid the collision of the $digest?

avanc
  • 11
  • 1
  • I don't understand. The jsFiddle you linked seems to work exactly as I would expect. – Brian Genisio Sep 02 '13 at 11:59
  • Please press "run" in the fiddler again. Sometimes it works for the first time, but not if tried again. Somehow a load/runtime issue. – avanc Sep 02 '13 at 12:41
  • I've run it a dozen times, all with the expected results (Chrome). What browser are you doing this in? – Brian Genisio Sep 02 '13 at 12:55
  • Good hint! I'm using Firefox 17.0.8. Will try it with the latest version at home. The result is that the first row is updated correctly ({{data}} but the directive is still "Initial". And I get the $digest error message. – avanc Sep 02 '13 at 14:41
  • @avanc you can try this solution. http://stackoverflow.com/a/17510699 – zs2020 Sep 02 '13 at 16:23
  • Also with Firefox 23.0.1. However, sometimes it works as expected. Don't know if it depends on how fast I click the alert windows. – avanc Sep 02 '13 at 16:27
  • @sza: Where to add the code? I don't use $apply at all. Shall wrap the $scope.data="Updated" altough it is already within $timeout? – avanc Sep 02 '13 at 16:31
  • @avanc What I don't understand is you asked the question about the alert() issue and later on you are talking about something totally different like delaying the page rendering. When you ask question, ask what you want to achieve, don't ask in a teasing way. – zs2020 Sep 02 '13 at 16:43

1 Answers1

0

You can use the solution posted here at stackoverflow.com/a/17510699 (Answered by @Dor Cohen).

I help you put it together with your current code.

var App = angular.module('myApp', []);

function Ctrl($scope, $timeout) {
    $scope.data = "Initial";
    $timeout(function () {
        $scope.data = "Updated"
    }, 1);

    $scope.alert = function (info) {
        alert(info);
    }

    $scope.safeApply = function (fn) {
        var phase = this.$root.$$phase;
        if (phase == '$apply' || phase == '$digest') {
            if (fn && (typeof (fn) === 'function')) {
                fn();
            }
        } else {
            this.$apply(fn);
        }
    };
}

App.directive('chart', function () {
    return {
        restrict: 'E',
        link: function (scope, elem, attrs) {
            scope.$watch(attrs.ngModel, function (v) {
                scope.safeApply(function () {
                    alert(v);
                });
                elem.html("Data=" + v);
            });
        }
    };
});
zs2020
  • 53,766
  • 29
  • 154
  • 219
  • Thanks for the hint. But I use the alert only to get some delays into the page. Otherwise, the problem would not occur. Let me see if I can trigger the problem in another way. – avanc Sep 02 '13 at 16:38
  • @avanc The problem was alert breaks angular's digest in firefox. And this approach can prevent it from happening. I totally don't understand what you want to achieve. If you want to just delay it, then you can use $timeout. – zs2020 Sep 02 '13 at 16:40
  • Still same behaviour (somtimes running, sometimes not): http://jsfiddle.net/cwesM/5/ – avanc Sep 02 '13 at 16:45
  • Don't use the safe apply based on $$phase ([relevant discussion here](http://stackoverflow.com/questions/12729122/prevent-error-digest-already-in-progress-when-calling-scope-apply/18996042#18996042)) because the $$ means that it's not future safe and that a future version of angular will likely break your code :( You can use $timeout for a safe apply if you need it. – betaorbust Feb 18 '14 at 18:24