If one changes a scope property first, and then broadcasts an event second, will the corresponding watcher callback and event listeners callback always be executed in that same order? For example:
$scope.foo = 3;
$scope.$broadcast('bar');
and elsewhere:
$scope.$watch('foo', function fn1(){...});
$scope.$on('bar', function fn2(){...});
Will fn1
always be executed prior to fn2
, or visa-versa, or can the order not be relied upon? Please cite sources, preferably to official angular docs.
In case it matters: lets assume the $scope.foo= and the $broadcast occur in a function invoked by an ng-click (i.e. user interaction)
[aside] Sorry question title is sloppy - please rename if you have something better.
@HostListener is Angular's decorator method that's used for listening to DOM events on the host element of both component and attribute directives.
An event listener is a function that initiates a predefined process if a specific event occurs. So, an event listener “listens” for an action, then calls a function that performs a related task. This event can take one of many forms. Common examples include mouse events, keyboard events, and window events.
Watchers keep an eye on both view element and model. So as soon as any change is made either in model or view element the watcher triggers the angular digest cycle which is responsible to update the value of model and view.
To understand what is happening, you need to understand Angular's $digest cycle and event $emit and $broadcast functions.
Based on some research, I've also learned that Angular does not use any kind of polling mechanism to periodically check for model changes. This is not explained in the Angular docs, but can be tested (see this answer to a similar question).
Putting all of that together, I wrote a simple experiment and concluded that you can rely on your event handlers running first, then your watch functions. Which makes sense, because the watch functions can be called several times in succession during the digest loop.
The following code...
template.html
<div ng-app="myApp">
<div watch-foo ng-controller="FooController">
<button ng-click="changeFoo()">
Change
</button>
</div>
</div>
script.js
angular.module('myApp', [])
.directive('watchFoo', watchFooDirective)
.controller('FooController', FooController);
function watchFooDirective($rootScope) {
return function postLink(scope) {
scope.$watch(function () {
return scope.foo;
}, function (value) {
console.log('scope.$watch A');
});
scope.$on('foo', function (value) {
console.log('scope.$on A');
});
$rootScope.$on('foo', function (value) {
console.log('$rootScope.$on A');
});
$rootScope.$on('foo', function (value) {
console.log('$rootScope.$on B');
});
scope.$on('foo', function (value) {
console.log('scope.$on B');
});
scope.$watch(function () {
return scope.foo;
}, function (value) {
console.log('scope.$watch B');
});
};
}
function FooController($scope) {
$scope.foo = 'foo';
$scope.changeFoo = function() {
$scope.foo = 'bar';
$scope.$emit('foo');
};
}
...yields the following results in the console when the 'Change' button is clicked:
scope.$on A
scope.$on B
$rootScope.$on A
$rootScope.$on B
scope.$watch A
scope.$watch B
UPDATE
Here is another test that illustrates the watch callback being called twice in the digest loop, but the event handlers not being called a second time: https://jsfiddle.net/sscovil/ucb17tLa/
And a third test that emits an event inside the watch function, then updates the value being watched: https://jsfiddle.net/sscovil/sx01zv3v/
In all cases, you can rely on the event listeners being called before the watch functions.
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