Reputation: 40886
I have been reading other people's answers as to how to use $watch properly in AngularJS (1.4) but I still don't get why my code does not work. I don't understand how to watch a model value from within the directive's link function. Setting scope.$watch(controller.value)
is only triggered before the value is bound to the controller (value still undefined), and later it is not triggered when the value changes. On the other hand, scope.$watch(function(){return controller.value})
works. Why is that?
In my example, I have an age value on the controller userCtrl
. I want to add class btn-primary
or btn-success
to a button based on the value of controller.age
: below is the directive:
app.directive('myDirective',function(){
return {
restrict: 'A',
templateUrl:'my-directive.html',
bindToController: true,
controller: 'userController',
controllerAs: 'userCtrl',
scope: {user:'='}, //example: {name:'John',age:9}
link: function(scope,element,attrs){
var vm = scope.userCtrl;
var el_button = element.find('button');
//WHY IS $WATCH NOT GETTING CALLED WHEN THE AGE CHANGES?
//if we change vm.user.age to function(){return vm.user.age} things work
//but why is that any different?
scope.$watch(vm.user.age,function(newVal,oldVal){
console.log('age has changed to ' + newVal);
if(newVal > 9){
el_button.removeClass('btn-primary');
el_button.addClass('btn-success');
}else{
el_button.removeClass('btn-success');
el_button.addClass('btn-primary');
}
});
}
}
});
Demo on Plunker: http://plnkr.co/edit/nFjiCzVhVJkUGY5AEM1C?p=preview
Upvotes: 1
Views: 737
Reputation: 123739
That is because the value returned by vm.user.age
at that point of time is considered as expression (or property here) to be evaluated against scope. If at that point of time say value is 12
then it will set a watch on property 12
on the scope, which is obviously not what you expect.
You should give either a string representation (scope.$watch('userCtrl.user.age', function(newVal...
) of the expression that can be evaluated against the scope or function that returns the value. When you provide userCtrl.user.age
to be watched, angular will wrap the expression in a getter function with the help of $parse service
, to say something like (just as example the getter function will be more complicated to tackle null checks etc..):
function(){
return $scope.userCtrl.user.age;
}
and that will be evaluated during the digest cycle for change detection. So providing a getter function yourself does not do any harm as well.
Upvotes: 1