Sammy S. Sammy S. - 4 months ago 7
AngularJS Question

Two different animations for route changes

I have the following case: I'm using the ui-router for the routing in my AngularJS application. In one route, there are five child states for different subscreens. I want to animate the transition between these in a carousel-like manner.

The navigation looks like this:

Link to A | Link to B | Link to C | Link to D | Link to E


Navigating from
state A
to
state B
should make
screen A
slide out to the left and
screen B
slide in from the right; vice versa for navigating from
state B
to
state A
.

What does work is animating the screen transitions with
transform: translateX(...);
on
enter
and
leave
in one direction only.

Usually, I control my animations using
ng-class
with a flag. In this case, however, setting a class on the
ui-view
element doesn't work at all (Angular 1.2 and ui-router 0.2 aren't completely compatible yet). Neither is it working with setting it with a custom directive listening to
scope.$on "$stateChangeStart"
which is fired after the transition has begun.

How can I implement the desired behavior?

Edit: The solution

For the record: I ended up implementing it using a custom
$scope
function using
$state.go()
to determine the direction before changing the route. This avoids the
$digest already in progress
errors. The class determining the animation is added to the
ui-view
's parent element; this animates both the current as well as the future
ui-view
in the correct direction.

Controller function (Coffeescript):

go: (entry) ->
fromIdx = ...
toIdx = ...

if fromIdx > toIdx
$scope.back = false
else
$scope.back = true

$state.go entry


Template:

<div ng-class="{toLeft: back}">
<div ui-view></div>
</div>

Answer

You can control the classes on your view by setting up a controller to do that specifically. You can then subscribe to events within the app and change the way the page animates.

<div class="viewWrap" ng-controller="viewCtrl">
  <div class="container" ui-view ng-class="{back: back}"></div>
</div>

Then within your controller

.controller('viewCtrl', function ($scope) {
    $scope.$on('$stateChangeSuccess', function (event, toState) {
        if (toState.name === 'state1') {
            $scope.back = true; 
        } else {
            $scope.back = false; 
        }
    });
});

I've set up a codepen to demonstrate here http://codepen.io/ed_conolly/pen/aubKf

For anybody trying to do this please note that I've had to use the ui.router.compat module due to the current incompatibility of the animations in Angular 1.2 and UI Router.

Comments