Monkey34
Monkey34

Reputation: 727

$scope.$watch not called, but $digest already in progress

I have an issue with a $scope.$watch not getting evaluated after a call to retrieve my data model. I'll explain, but for the impatient, maybe you can figure out what's going on based on this fiddle: http://jsfiddle.net/ADukg/3964/.

In our Angular project, we let some directives build their own data model, which is also sometimes passed down to other, contained directives. In this case, the directive building its own model is myMessages, and the directive to which its data is passed is myObjects.

The myMessages directive relies on a service called messageManager to get the messages relevant for it at any time (in our code, this is based on things like $routeParams values, e.g., http://localhost/messages/34). For my example, I just ignored the parameters and passed back the data directly.

The interesting part is that, if I comment out lines 55 and 68 (the wrapping setTimeout call), this code works "fine" (I put it in quotes because, well, I'm really not sure if it's actually using good practices or not). However, keeping the call there to simulate the HTTP request, the value of the objects scope variable in the myObjects directive's link method is empty, and the watch set up against the variable fails to run after 2 seconds.

I tried to trick the watch to re-evaluate by explicitly calling $scope.$apply both around the call to ctrl.loadData() and inside the actual loadData method, but received this error:

$digest already in progress

Looking through the docs, it seems as though that's correct, because we're still within the Angular context when we do these calls (we're using ngResource inside a service, so it's not calling out to some jQuery context or anything strange). I just can't seem to get the watch against the objects variable to re-evaluate after the data has been populated, no matter what I try (including setting the third parameter to the $scope.$watch function to true).

Any help would be very much appreciated.

Upvotes: 2

Views: 593

Answers (1)

zs2020
zs2020

Reputation: 54514

You should use $timeout instead of setTimeout since $timeout will trigger a digest cycle to make the data available in the binding.

myApp.factory('messageManager', function ($q, $timeout) {
    return {
        getMessages: function () {
            var deferred, promise;

            deferred = $q.defer();
            promise = deferred.promise;

            $timeout(function () {
                deferred.resolve({
                    messages: [{
                        firstName: 'Sam',
                        lastName: 'Rockwell',
                    }, {
                        firstName: 'Nat',
                        lastName: 'Faxon',
                    }, {
                        firstName: 'Jim',
                        lastName: 'Rash'
                    }]
                });
            }, 2000);

            return promise;
        }
    };
});

Upvotes: 2

Related Questions