Today I encounter some really strange behaviour in AngularJS using $watch. I simplified my code to the following example:
https://jsfiddle.net/yLeLuard/
This example contains a service which will keep track of a state
variable. The directives are used to bind a click event to the DOM changing the state
variable through the service.
There are two problems in this example:
main.html
<div ng-controller="main">
State: {{ api.isOpen | json }}
<div ng-click="" open>
<button>Open - Working fine</button>
</div>
<div ng-click="" close>
<button>Close - Works, but only on second click</button>
</div>
<div open>
<button>Open - Not Working</button>
</div>
<div close>
<button>Close - Not Working</button>
</div>
</div>
main.js
var myApp = angular.module('myApp', []);
myApp.controller('main', ['$scope', 'state', function($scope, state) {
$scope.api = state;
$scope.api.isOpen = false;
$scope.$watch('api.isOpen', function() {
console.log('state has changed');
});
}]);
myApp.directive('open', ['state', function(state) {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<button>Open</button>',
link: function(scope, element) {
element.on('click', function() {
state.isOpen = true;
});
}
};
}]);
myApp.directive('close', ['state', function(state) {
return {
restrict: 'A',
scope: {},
replace: true,
template: '<button>Close</button>',
link: function(scope, element) {
element.on('click', function() {
state.isOpen = false;
});
}
};
}]);
myApp.service('state', function() {
return {
isOpen: null
};
});
That's because you are using a native event listener on click
. This event is asynchronous and out of the Angular digest cycle, so you need to manually digest the scope.
myApp.directive('open', ['state', function(state) {
return {
restrict: 'A',
scope: {},
link: function(scope, element) {
element.on('click', function() {
scope.$apply(function() {
state.isOpen = true;
});
});
}
};
}]);
Fixed fiddle: https://jsfiddle.net/7h95ct1y/
I would suggest changing the state directly in the ng-click: ng-click="api.isOpen = true"
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