I created a directive for showing tooltips:
app.directive('tooltip',function(){
return{
restrict: 'A',
link: function(scope,element,attr){
element.bind('mouseenter',function(e){
scope.setStyle(e);
});
}
}
});
The corresponding setStyle()
function:
$scope.setStyle = function(e){
$scope.style = {
position: 'absolute',
// some other styles
};
$scope.$digest();
};
$scope.style
is applied to this:
<span ng-style="style">I am a tooltip</span>
which is part of my view, handled by the controller who owns $scope.style
Why do i have to call $digest()
in order to apply the changes to $scope.style
, which was declared and initialized earlier?
At key points in your application AngularJS calls the $scope. $digest() function. This function iterates through all watches and checks if any of the watched variables have changed. If a watched variable has changed, a corresponding listener function is called.
$digest() gets called without any arguments. $apply() takes a function that it will execute before doing any updates. The other difference is what they affect. $digest() will update the current scope and any child scopes.
$digest() is faster than $apply(), since $apply() triggers watchers on the entire scope chain while $digest() triggers watchers on the current scope and its children(if it has).
There are a few ways to deal with this. The easiest way to deal with this is to use the built in $timeout, and a second way is if you are using underscore or lodash (and you should be), call the following: $timeout(function(){ //any code in here will automatically have an apply run afterwards });
The $scope.$digest () function iterates through all the watches in the $scope object, and its child $scope objects (if it has any). When $digest () iterates over the watches, it calls the value function for each watch.
Note: $scope.$apply () automatically calls $rootScope.$digest (). The $apply () function comes in two flavors. The first one takes a function as an argument, evaluates it, and triggers a $digest cycle. The second version does not take any arguments and just starts a $digest cycle when called.
At key points in your application AngularJS calls the $scope.$digest () function. This function iterates through all watches and checks if any of the watched variables have changed. If a watched variable has changed, a corresponding listener function is called.
The $scope.$apply () function takes a function as parameter which is executed, and after that $scope.$digest () is called internally. That makes it easier for you to make sure that all watches are checked, and thus all data bindings refreshed.
Because the callback attached to the mouseenter
event is outside of angular's scope; angular has no idea when that function runs/ends so the digest cycle is never ran.
Calling $digest
or $apply
tells angular to update bindings and fire any watches.
element.bind()
means listen for specific browser event and execute callback when this event is dispatched on element. Nowhere in this chain of events Angular is included - it does not know that the event happened. Therefore you must tell it about the event explicitly. However, in most cases you should use $scope.$apply()
not $scope.$digest()
, especially when you're not sure about it.
Here is more appropriate code for your situation:
app.directive('tooltip',function(){
return{
restrict: 'A',
link: function(scope,element,attr){
element.bind('mouseenter',function(e){
scope.setStyle(e);
scope.$apply();
});
}
}
});
and setStyle()
:
$scope.setStyle = function(e){
$scope.style = {
position: 'absolute',
// some other styles
};
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With