Search code examples
javascriptcssangularjsng-animate

$animate.enter() is not working with custom directive


having trouble with angular-animate 1.4.3... the leave animation for a directive works fine, but the enter animation does not; from what I can see, the classes ng-enter and ng-enter-active are not being applied to the element. Why is this? Here's a plunkr of it

script.js

(function() {
  'use strict';

  // ---------------Module----------------

  angular.module('app', ['ngAnimate'])
    .run(['fooService', function(fooService) {
      fooService.foo('Hello World!');
    }]);

  // --------------Service----------------

  function fooService($rootScope, $compile, $animate, $timeout) {

    function createDirective(message) {
      var newFoo = {
        scope: $rootScope.$new()
      };

      var target = angular.element(document.querySelector('#bar'));
      var elem = angular.element(document.createElement('foo'));

      newFoo.scope.message = message;

      newFoo.elem = $compile(elem)(newFoo.scope);

      $animate.enter(newFoo.elem, target).then(function() {
        $timeout(function() {
          removeDirective(newFoo);
        }, 3000);
      });
    }

    function removeDirective(foo) {
      $animate.leave(foo.elem).then(function() {
        foo.scope.$destroy();
      });
    }


    function foo(message, overrides) {
      return createDirective(message);
    }

    return {
      foo: foo
    };
  }

  fooService.$inject = ['$rootScope', '$compile', '$animate', '$timeout'];

  angular.module('app')
    .factory('fooService', fooService);

  // -------------Directive---------------

  function fooDirective() {
    return {
      restrict: 'AE',
      replace: true,
      templateUrl: 'foo.html',
      link: function(scope) {
      }
    }
  }

  angular.module('app')
    .directive('foo', fooDirective);

}());

foo.html

<div class="alert alert-success">
  {{message}}
</div>

index.html

<!DOCTYPE html>
<html ng-app="app">

  <head>
    <link data-require="[email protected]" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
    <script data-require="angular.js@~1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
    <script data-require="[email protected]" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular-animate.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <div id="bar"></div>
  </body>

</html>

style.css

.ng-leave, .ng-enter {
  transition: all 1s ease-out;
}

.ng-enter.ng-enter-active, .ng-leave {
  opacity: 1;
}

.ng-enter, .ng-leave.ng-leave-active {
  opacity: 0;
}

Appreciate any help with this


Solution

  • Angular doesn't link the directives on compilation. You can read a little about this here.

    The implications for this case result in linking after $animate.enter(). You can wrap this call into a timeout but this is not a sustainable solution since this time varies dependent on how busy browser is and whether it's first time the directive is linked for this app. So I only see an approach based on promises. Due to the scope injection that is already in place for the custom directive, the latter can resolve the promise provided from the fooService

      newFoo.scope.deferred = $q.defer();
      newFoo.scope.deferred.promise.then(function(){
        console.log('$animate.enter');
        $animate.enter(newFoo.elem, target).then(function() {
          console.log('$animate.entered');
          $timeout(function() {
            removeDirective(newFoo);
          }, 3000);
        });
      });
    

    And in directive the promise should be resolved on link

      link: function(scope) {
        console.log('foo.link');
        scope.deferred.resolve();
      }
    

    Updated Plunker