I know I am asking a question that has an extremely simple answer (I am new to angular and it did take a lot of courage to ask this here ;). I have a directive that displays the time like this:
Application.Directives
.directive('theClock', function(){
return {
retrict: 'EA',
templateUrl: 'partials/partial.clock.html',
link: function(scope, elem, attrs) {
scope.dTime = Date();
scope.$watch( function(scp){ return Date(); }, function(newVal){
scope.dTime = newVal;
}
);
}
};
});
I expect that the time would update every $digest cycle but it does not. I know that using setInterval
ot $timeout
is another way to do this, but I dont really want a clock, I want to understand why the update does not happen. BTW I did call $apply()
after checking for $$phase
- did not help
Thank you.
The $watch function compares objects by reference by default, which is faster and fine for string/number values, but won't work for complex objects like Date - the Date may change, but it is allways the same Date object, so the reference doesn't change.
This is to avoid heavy equality tests on gigantic arrays.
Now, while this may work (note the third parameter of the $watch function set to true):
scope.dTime = new Date();
scope.$watch( function(scp){ return new Date(); }, function(newVal){
scope.dTime = newVal;
}, true
);
It will probably trigger a change event every time it polls the $watch function (meaning every single app cycle)
If you want if to trigger every second, return the seconds from Date(), with something like return Math.floor(new Date().valueOf()/1000). Then set dTime as you wish.
Update
While angular's checking would work with return Date()
, the problem is due to angular's dirty checking system - it will only check the $watch functions when a change is triggered on a model (meaning a model defined within a $scope).
This Plnkr, forked from Valentyn Shybanov's, can serve as an example - a clock function triggers a change on a model, and that change triggers a $watch function.
ALSO, notice this version, that uses window.setTimeout. It can only work using an $apply method to notify angularJS that a change has occured.
"I want to understand why the update does not happen."
I think this picture (from the Conceptual Overview page) explains it best:
The $watch list is not examined unless $apply() is called. $apply is how we enter the "$digest loop". Many (all?) Angular built-in directives automatically call $apply (so we don't have to). E.g., as @Tiago already mentioned, changing a bound $scope property (inside Angular) will result in $apply being called. We can also explicitly call $apply ourselves (e.g., from inside a 3rd-party library callback function we define -- if we don't call $apply here, we won't enter the $digest loop).
Bottom line: unless we get into the blue box (the "Angular execution context"), the $watch list is not examined -- so dirty checking is not performed.