Mike Pateras
Mike Pateras

Reputation: 15015

AngularJS Directive scope not binding in time

I'm having a binding timing issue with my AngularJS directive. Its controller looks like this:

controller: function($element, $scope)
{
    $element.find("input").bind("blur", function()
    {
        SendUpdate();
    });

    $scope.Subtract = function()
    {
         Add(-$scope.step);
    }

    $scope.Add = function()
    {
        Add($scope.step);
    }

    function Add(amount)
    {
        $scope.model = parseInt($scope.model) + parseInt(amount);
        console.log("Model: " + $scope.model);
        SendUpdate();
    }

    function SendUpdate()
    {
        $scope.$emit("ProcessUpdate");
    }
}

Everything works properly thus far, and start at a value of 100 and adding 10, it prints out Model: 110, as expected. But, when the event is handled by the parent scope, which is providing the variable that model is bound to, it has not received the updated value by the time the event fires:

$scope.$on("ProcessUpdate", function()
{
    console.log("MyValue: " + $scope.MyValue);
});

That prints out MyValue: 100, even though it's $scope.MyValue that is bound to the directive's model variable (I'm using the "=" binding character, too).

The value is, in fact, being updated. If I press a button that prints out the same exact thing:

console.log("MyValue: " + $scope.MyValue);

It prints the correct value of MyValue: 110. So it's clearly a timing issue. It looks like things are happening in this order:

  1. Update the directive's $scope.model.
  2. Fire the event.
  3. Handle the event.
  4. Update the parent's $scope.model.

What I need is for 4 to happen immediately after 1, because when the event is fired I need the parent scope to be up to date.

Is there a different way that I should be doing this? I don't want to pass the updated value via the event because any number of these directives could be firing and they need to all process the parent scope. I don't want to have to figure out what changed and inject it accordingly. I just want to fire the event after the parent scope has received the directive's new value.

Thank you.

Upvotes: 2

Views: 1784

Answers (2)

Allan S.
Allan S.

Reputation: 84

Try the code below

$scope.$on("ProcessUpdate", function() {
    $timeout(function() {
        console.log("MyValue: " + $scope.MyValue);
    });
});

Even though the timeout is zero, it does not execute until interpolation and DOM rendering is complete. That behavior is explained in more detail here:

http://ejohn.org/blog/how-javascript-timers-work/

Hope that works for you :)

Upvotes: 6

rtcherry
rtcherry

Reputation: 4880

The problem is you are using events (which are immediate) and two-way binding uses the Angular $digest loop. How about instead of using $on and $emit you use a $watch function instead?

In your directive, you would do this:

$scope.$watch("MyValue", function(newValue, oldValue) {
  console.log("MyValue: " + $scope.MyValue);
});

And in your controller all you have to do is remove SendUpdate.

Upvotes: 2

Related Questions