TL;DR
Using $timeout or $apply, or $evalAsync in the callback function of $on, if you need to handle some bound model and have to make sure you get it before or after it's updatedI met this one when I was trying to save map data from a google map view whenever user changes the map view.
The code somewhat looks like this.
// In Map controller ...
// ... bind google map's control data to mapModel, so mapModel will update after each digest cycle.
// ... bind google map event "idle" to onIdle function
function onIdle() {
// Broadcast event after each user interaction to the map
$rootScope.$broadcast('map:idle', mapModel);
}
// In some other controller ...
$scope.$on('map:idle', function(event, mapModel){
console.log(mapModel);
saveMap(mapModel);
});
I expected to get map state after I panned or zoomed the map, but instead I got the "previous one" of map state, before mapModel is updated.
The order of what happened is like:
- User changes map view.
- Process event and call handler.
- Digest cycle updates mapModel
A simple fact comes out here:
The Angular event flow, $broadcast, $emit, $on, don't sync with the digest cycle.
The Angular event flow, $broadcast, $emit, $on, don't sync with the digest cycle.
Which means the executions of $broadcast, $emit and the registered callbacks of $on won't be scheduled before, in, or after Angular's digest cycle.
It's a bit hard for me to realize this since I reckoned most of the Angular built-in functions do sync with digest cycle implicitly.
Well it's alright whatever, I found the fast workaround is to use $timeout to get the updated map model, because $timeout always trigger it's registered function after the current digest cycle completed
// In some other controller ...
$scope.$on('map:idle', function(event, mapModel){
$timeout(function () {
saveMap(mapModel);
}, 0, false);
});
Note the third parameter of $timeout is false because I don't want to invoke another digest cycle.
No comments:
Post a Comment