We know angularjs has a data binding between scope and html, which means what ever data changes we make in our controller scope it gets reflected to our html. But things change when we use a 3rd party library or external events to make changes to our scope.
Lets take an example to see how this happens
In the above, code I have taken a simple scope variable “text” and used jquery library to demonstrate an outside function. So using jquery’s click function, when you click on the “Click Me” button i have changed the scope “text” variable to current date. If you run the code you will see that it will not work.
Access Angular Scope From Outside Function or 3rd Party Library
AngularJS exposes a method “$apply” for this purpose. This function is used to inform angular that changes have been made to $scope variable from an outside function or event.
Here is code for the same
Now we see Date gets updated on button click.
The only change made to above code was
$scope.$apply(function(){
$scope.text = new Date();
});
So we made the $scope changes inside the $apply function. This tell answer to run the $digest cycle to check for any changes in $scope.
Difference Between $apply and $apply(fn)
There are two ways to use the $apply function, taking the above example
Method1:
$scope.$apply(function(){
$scope.text = new Date();
});
Method2:
$scope.text = new Date();
$scope.$apply();
In most cases both the above will give you the same desired effect, but there are difference between the two. As per angular documentation, pseduo code for $apply is
function $apply(expr) {
try {
return $eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
So the Method1 takes care of exception handling and always run the digest cycle. So method1 is always preferred to method2.
$injector : Accessing Angular Services Outside Scope
Let’s take the above one step further, lets go further outside scope 🙂
In the previous example, we had put in our ‘click’ inside angular controller. What if we put click event outside the controller, like in document.ready event. In such cases we need to angularjs ‘$injector’ to access instances of angular services. Lets look at code to understand it better
In above we are take $injector object from ng-app and from $injector we can access any of the existing services. Current i am using $rootScope but the same code can be extended to any service.
$('#click_button').click(function(){
alert('Click!');
var elem = angular.element(document.querySelector('[ng-app]'));
var injector = elem.injector();
var $rootScope = injector.get('$rootScope');
$rootScope.$apply(function(){
$rootScope.text = new Date();
});
});
A much more detailed usage of $injectors given here http://stackoverflow.com/a/22467028/2304347