Why is ng-class based ng-animate transition not working?
Asked Answered
D

1

1

This is a heavily simplified demo of ng-class animation using transition that I'm trying to achieve.

I'm trying to create a simple fade in animation when an item is selected.

Selecting an item will add a selected class on it using ng-class directive. I'm trying to add a transition to it following the documentation with the animation hook classes added by ng-animate.

Below is the code I have so far. It sets the opacity to 0 along with the transition property, and when the .selected-add-active class is added it is supposed to transition to opacity 1. But it does not work.

angular.module('demo', ['ngAnimate'])
  .controller('demoCtrl', function($scope) {
    $scope.selected = false;
    $scope.selectToggle = function() {
      $scope.selected = !$scope.selected;
    };
  });
.item {
  width: 50px;
  height: 50px;
  background: grey;
}
.item.selected {
  background-color: dodgerblue;
}
.item.selected.selected-add {
  transition: opacity 3s;
  opacity: 0;
}
.item.selected.selected-add.selected-add-active {
  opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-animate.js"></script>
<div ng-app="demo" ng-controller="demoCtrl">
  <div class="item" ng-class="{selected:selected}"></div>
  <br>
  <br>
  <button ng-click="selectToggle();">
    {{selected? 'Unselect' : 'Select'}}
  </button>
</div>

Why isn't my code working?

Dissertation answered 30/6, 2016 at 13:41 Comment(0)
D
4

The problem here seems to be the order in which angular ng-animate applies the CSS classes and how browser optimizes DOM reflows.

ng-animate initially adds the x-add class prior to actually adding class x. This is where it forces(or fakes) a DOM reflow so that browser detects the initial state for animation.

But since the selector for initial state's CSS (.item.selected.selected-add) is chained with .selected class which isn't added yet, it doesn't match anything and browser ignores it.

After this, ng-animate adds the selected class followed by selected-add-active class. At this point the CSS selector for initial state does match, but these changes happens in the same reflow, hence the browser chooses to apply opacity from the selector with highest specificity which is

.item.selected.selected-add.selected-add-active {
  opacity: 1;
}

Now since no actual change in the element's opacity is detected, no transition takes place.


To fix this, avoid chaining the animation hook classes added by ng-animate with the class being toggled. Especially the x-add class which should be detected by the browser before the following classes gets applied.

Below is a working example:

angular.module('demo', ['ngAnimate'])
  .controller('demoCtrl', function($scope) {
    $scope.selected = false;
    $scope.selectToggle = function() {
      $scope.selected = !$scope.selected;
    };
  });
.item {
  width: 50px;
  height: 50px;
  background: grey;
}
.item.selected {
  background-color: dodgerblue;
}
.item.selected-add {
  transition: opacity 3s;
  opacity: 0;
}
.item.selected-add.selected-add-active {
  opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-animate.js"></script>
<div ng-app="demo" ng-controller="demoCtrl">
  <div class="item" ng-class="{selected:selected}"></div>
  <br>
  <br>
  <button ng-click="selectToggle();">
    {{selected? 'Unselect' : 'Select'}}
  </button>
</div>
Dissertation answered 30/6, 2016 at 13:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.