marked-down
marked-down

Reputation: 10408

Angular variable not updating, attempting to call $apply results in "digest already in progress" error

I have a bunch of properties on an object that are determined dynamically:

$scope.buttons.isVisible = {
                buttonOne:      $scope.timeBetween > -(60 * 60) && $scope.timeBetween < 30,
                buttonTwo:      $scope.timeBetween > -(60 * 15) && $scope.timeBetween < -(60 * 8),
                buttonThree:    $scope.timeBetween > -(60 * 15)
}

I use these variables to determine the visibility of some buttons on a page:

<button class="canned-response" ng-if="buttons.isVisible.buttonOne">Button Text</button>

The variable $scope.timeBetween is updated once per second inside of a function which is a callback of a countdown directive:

$scope.setTimeBetween = function(relativeSecondsBetween) {
    $scope.timeBetween = relativeSecondsBetween;
};

An example of the usage of the directive:

<countdown countdown-to="someVar" callback="setTimeBetween"></countdown>

The issue is that changes to $scope.timeBetween are being made, but those changes are not propagating through to my button visibility booleans.

If I try and call $scope.$apply() at the end of the setTimeBetween function, I get a "digest already in progress" error. Wrapping the variable assignment in a $timeout does nothing either.

What's the best solution to this?

Upvotes: 0

Views: 65

Answers (2)

Shaun Scovil
Shaun Scovil

Reputation: 3987

You need to watch $scope.timeBetween for changes, then update your button visibility object.

$scope.$watch(function() {
    return $scope.timeBetween;
}, function(newValue, oldValue) {
    $scope.buttons.isVisible = {
        buttonOne: newValue > -(60 * 60) && newValue < 30,
        buttonTwo: newValue > -(60 * 15) && newValue < -(60 * 8),
        buttonThree: newValue > -(60 * 15)
    };
});

Also, as a best practice, you should set up this watch in your directive's link function, not the controller.

Upvotes: 1

masa
masa

Reputation: 2800

The button visibility booleans are evaluated only once.

One solution is to define them as functions:

buttonOne: function () {
    return $scope.timeBetween...;
}

And then call the functions in ng-if:

ng-if="buttons.isVisible.buttonOne()"

Upvotes: 1

Related Questions