Search code examples
angularjsanimationng-animate

Angular $animate misunderstanding


There's something I'm missing in AngularJS $animate. I have no idea if my error is trivial or a massive misunderstanding. I tried to cut this down to the minimum example but it's still quite large. At least, however, it should run out of the box.

<!doctype html>
<html data-ng-app="MyModule">

<head>
  <style type="text/css">
    .msg.ng-leave {
      transition: all 1s;
      opacity: 1;
    }
    
    .msg.ng-leave.ng-leave-active {
      opacity: 0;
    }
    
    .fade.ng-enter {
      transition: opacity 1s;
      opacity: 0;
    }
    
    .fade.ng-enter.ng-enter-active {
      opacity: 1;
    }
  </style>

</head>

<body data-ng-controller="MyController">
  <div class="navpanel">
    <p class="msg"><strong>This is a message, make it go away</strong></p>
    <h3>Menu</h3>
    <ul id="menulist">
      <li>Do This</li>
      <li>Do That</li>
    </ul>
  </div>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
  <script>
    angular.module('MyModule', ['ngAnimate'])
      .controller('MyController', ['$scope', '$animate', function($scope, $animate) {
        let npanel = angular.element(document.querySelector('.navpanel'));
        let msg = angular.element(document.querySelector('.msg'));
        let menulist = angular.element(document.getElementById('menulist'));
        let counter = 0;
        npanel.on('click', function() {
          $animate.leave(msg);

          let newpara = angular.element(document.createElement('p'));
          newpara.html('new paragraph ' + (++counter)).addClass('fade');

          let children = npanel.children();
          $animate.enter(angular.element(newpara), npanel, menulist);
        });
      }]);
  </script>
</body>

</html>

It's intended to do two main things on clicking. One, it should remove the message, two, it should insert a new paragraph. It does the second (using $animate.enter) but the first fails completely (using $animate.leave).

$animate.leave seems, from the documentation, to be supposed to run through any animation, then remove the item from the DOM, but my code does none of that. The $animate.enter by contrast, does not succeed in performing any animations, but does manage to insert the new item (which seems to imply that $animate has been properly injected).

What am I missing?


Solution

  • on is a jqLite/jQuery method and will not trigger AngularJS' digest cycle. This means that when you click on the element the attached event listener will execute, the DOM operations will be performed, but since the digest cycle doesn't start AngularJS' animation hooks will not handle the animation.

    You can use $apply:

    $apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

    For example:

    npanel.on('click', function() {
      $scope.$apply(function() {
        $animate.leave(msg);
    
        let newpara = angular.element(document.createElement('p'));
        newpara.html('new paragraph ' + (++counter)).addClass('fade');
    
        let children = npanel.children();
        $animate.enter(angular.element(newpara), npanel, menulist);
      });
    });