I am having some weird issue with ng-class
, and I am suspecting that it has to do with race condition.
Here is plunker example
Here is the relevant js code
self.slideLeft = function() {
if (self.end_index < self.list_of_stuff.length) {
self.direction = 'left';
debugger;
self.start_index = self.start_index + 4;
self.end_index = self.end_index + 4;
self.display_list = self.list_of_stuff.slice(self.start_index, self.end_index);
}
}
self.slideRight = function() {
if (self.start_index > 0) {
self.direction = 'right';
debugger;
self.start_index = self.start_index - 4;
self.end_index = self.end_index - 4;
self.display_list = self.list_of_stuff.slice(self.start_index, self.end_index);
}
}
Here is the relevant html
<div class="stuff-wrapper">
<div class="stuff"
ng-class="bCtrl.directionClass()"
ng-repeat="stuff in bCtrl.display_list">
{{stuff}}
</div>
</div>
Here is the animation.
.left.ng-enter,
.left.ng-leave {
-webkit-transition: all 1s ease-in-out;
}
.left.ng-enter {
transition-delay: 0.7s;
opacity: 0;
left: 10%;
}
.left.ng-enter-active {
opacity: 1;
left: 0;
}
.left.ng-leave {
opacity: 1;
left: -10%;
}
.left.ng-leave-active {
left: -20%;
opacity: 0;
}
This is a simple app that slide a list of number left and right.
If the left button is pressed, the numbers slide left.
If the right button is pressed, the numbers slide right.
But we see that if there are a change of direction, the number will first slide in the wrong direction, and the subsequent direction will be correct.
I suspected that this is due to race condition.
Indeed, I see that ng-class
does not get applied right after I changed the direction self.direction
using the debugger.
This is very curious.
Is there a way to combat this?
To quote the answer from this (https://stackoverflow.com/a/21664152/2402929) question:
You need to make the changes to the $scope.elements after the css class is updated at the DOM. So you need to delay the DOM manipulation for one digest loop. This can be done by the $timeout service (please see this answer for more information AngularJS $evalAsync vs $timeout):
Your elements are being removed in the same digest loop as you're updating the css classes. Meaning - the css does not get updated and the elements just get removed.
The digest loop will encompass your entire ng-click function, because with all angular built-in directives the code is wrapped inside a $scope.$apply call.
To clarify:
$scope.$apply() takes a function or an Angular expression string, and executes it, then calls $scope.$digest() to update any bindings or watchers.
You can read more about it here (http://jimhoskins.com/2012/12/17/angularjs-and-apply.html).
So what solves your problem is the wrapping the removal of data from your array into a $timeout block, which will delay the DOM manipulation for one digest loop and separate the changing of the classes and the removal of data:
self.slideLeft = function() {
if (self.end_index < self.list_of_stuff.length) {
self.direction = 'left';
self.start_index = self.start_index + 4;
self.end_index = self.end_index + 4;
$timeout(function(){
self.display_list = self.list_of_stuff.slice(self.start_index, self.end_index);
});
}
}
self.slideRight = function() {
if (self.start_index > 0) {
self.direction = 'right';
self.start_index = self.start_index - 4;
self.end_index = self.end_index - 4;
$timeout(function(){
self.display_list = self.list_of_stuff.slice(self.start_index, self.end_index);
});
}
}
And here is a working plunker: http://plnkr.co/edit/30wJhL?p=preview
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