How can I tell AngularJS to "refresh"

I have a click event that happens outside the scope of my custom directive, so instead of using the "ng-click" attribute, I am using a jQuery.click() listener and calling a function inside my scope like so:

$('html').click(function(e) {
  scope.close();
);

close() is a simple function that looks like this:

scope.close = function() {
  scope.isOpen = false;
}

In my view, I have an element with "ng-show" bound to isOpen like this:

<div ng-show="isOpen">My Div</div>

When debugging, I am finding that close() is being called, isOpen is being updated to false, but the AngularJS view is not updating. Is there a way I can manually tell Angular to update the view? Or is there a more "Angular" approach to solving this problem that I am not seeing?


Solution 1:

The solution was to call...

$scope.$apply();

...in my jQuery event callback.

Solution 2:

Why $apply should be called?

TL;DR: $apply should be called whenever you want to apply changes made outside of Angular world.


Just to update @Dustin's answer, here is an explanation of what $apply exactly does and why it works.

$apply() is used to execute an expression in AngularJS from outside of the AngularJS framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the AngularJS framework we need to perform proper scope life cycle of exception handling, executing watches.

Angular allows any value to be used as a binding target. Then at the end of any JavaScript code turn, it checks to see if the value has changed. That step that checks to see if any binding values have changed actually has a method, $scope.$digest()1. We almost never call it directly, as we use $scope.$apply() instead (which will call $scope.$digest).

Angular only monitors variables used in expressions and anything inside of a $watch living inside the scope. So if you are changing the model outside of the Angular context, you will need to call $scope.$apply() for those changes to be propagated, otherwise Angular will not know that they have been changed thus the binding will not be updated2.

Solution 3:

Use

$route.reload();

remember to inject $route to your controller.

Solution 4:

While the following did work for me:

$scope.$apply();

it required a lot more setup and the use of both .$on and .$broadcast to work or potentially $.watch.

However, the following required much less code and worked like a charm.

$timeout(function() {});

Adding a timeout right after the update to the scope variable allowed AngularJS to realize there was an update and apply it by itself.