Question

I am trying to apply animations to ng-view (routing) depending of the views involved.

For example, from View1 to View2 I need the View1 leaving through the left side and View1 entering from the right side. Otherwise, from View2 to View1 I need View2 leaving through the right side and View1 entering from the left side.

But I have also situations where I need apply different animations to both views, for example, View1 leaving fading out and View2 entering scaling up.

What I am doing is using a scope associated variable as class in the ng-view:

<div ng-view class="{{transition}}"></div>

This variable is set in each route change with something like this in each controller:

$scope.transition=Global.transition;

$rootScope.$on("$routeChangeStart",function (event, current, previous) {
   // Here I get the leaving view and the entering view and the kind of transition is selected
   ...
   $scope.transition=selectedLeavingTransition;  // Set the transition for the leaving view
   Global.transition=selectedEnteringTransition; // Set the transition for the entering view
});

Global is a service to set the transition variable for the entering scope from the leaving scope.

This way, when a route change is detected, the current ng-view is set with the class associated to selectedLeavingTransition, and the entering ng-view is set with the class associated to selectedEnteringTransition.

For example, if the route change was from View1 to View2 the ng-views during the animation could be:

<div ng-view class="fadeOut ng-animate ng-leave ng-leave-active"></div>
<div ng-view class="scaleUp ng-animate ng-enter ng-enter-active"></div>

The CSS in this case could be:

fadeOut.ng-leave {animation:1s fadeOut;}
scaleUp.ng-enter {animation:1s scaleUp;}

Though it works, I am wondering if there is a simpler way to do it as it seems a little mess.

Was it helpful?

Solution

An alternative solution that doesn't require much code is to define your animations on your routes:

$routeProvider.when('/view1', {
  templateUrl: 'view1.html',
  controller: 'View1Controller',
  animations: {
    enter: 'enter-left',
    leave: 'leave-left'
  }
});

Then use a directive to retrieve the current route's animations and add them to the element:

app.directive('viewAnimations', function ($route) {
  return {
    restrict: 'A',
    link: function (scope, element) {
      var animations = $route.current.animations;
      if (!animations) return;

      if (animations.enter) element.addClass(animations.enter);
      if (animations.leave) element.addClass(animations.leave);
    }
  };
});

And put the directive on the element that contains the ngView directive:

<body ng-view view-animations></body>

Demo: http://plnkr.co/edit/Y3ExDyiPIJwvVKO4njBT?p=preview

Edit: New solution.

To set animations during run-time I would use a service just like you are doing, but a directive to apply them.

Very basic example of service:

app.factory('viewAnimationsService', function ($rootScope) {

  var enterAnimation;

  var getEnterAnimation = function () {
    return enterAnimation;
  };

  var setEnterAnimation = function (animation) {
    enterAnimation = animation;
  };

  var setLeaveAnimation = function (animation) {
    $rootScope.$emit('event:newLeaveAnimation', animation);
  };

  return {
    getEnterAnimation: getEnterAnimation,
    setEnterAnimation: setEnterAnimation,
    setLeaveAnimation: setLeaveAnimation
  };
});

And the directive:

app.directive('viewAnimations', function (viewAnimationsService, $rootScope) {
  return {
    restrict: 'A',
    link: function (scope, element) {

      var previousEnter, previousLeave;

      var enterAnimation = viewAnimationsService.getEnterAnimation();
      if (enterAnimation) {
        if (previousEnter) element.removeClass(previousEnter);
        previousEnter = enterAnimation;
        element.addClass(enterAnimation);
      }

      $rootScope.$on('event:newLeaveAnimation', function (event, leaveAnimation) {
        if (previousLeave) element.removeClass(previousLeave);
        previousLeave = leaveAnimation;
        element.addClass(leaveAnimation);
      });
    }
  };
});

Demo: http://plnkr.co/edit/DuQXaN2eYgtZ725Zqzeu?p=preview

OTHER TIPS

I have been working on it and I have a neater solution, what I was doing had some problems. Now I am just using the $routeChangeStart at root scope and selecting there the leaving and enter transitions.

The only problem I have is that on the routeChangeStart event I can't modify the leaving view so I can't establish the leaving transition to the ngView element class attribute. I had to set it directly through the DOM (I know that is bad practice).

I tried to modify the leaving view through a shared service, the root scope and $apply() but none of them worked. Once the routeChangeStart event is launched the view seems static.

Here is a working example: jsfiddle.net/isidrogarcia/Fs5NZ

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top