topherlicious
topherlicious

Reputation: 173

Angular 4 :leave animation not triggering

In my project, I have the following:

slideInOut.ts

module import { trigger, state, animate, transition, style } from '@angular/animations';

export const slideInOut = trigger('slideInOut', [
  state('*', style({
    transform: 'translateX(0)'
  })),

  // route 'enter' transition
  transition(':enter', [
    style({
      transform: 'translateX(100%)'
    }),
    animate('.3s ease-in-out', style({
      transform: 'translateX(0)'
    }))
  ]),

  // route 'leave' transition
  transition(':leave', [
    animate('.3s ease-in-out', style({
      transform: 'translateX(100%)'
    }))
  ])
]);

home.component.ts

import { Component } from '@angular/core';
import { slideInOut } from '../../animations/slideInOut';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  animations: [
    slideInOut
  ],
  styleUrls: ['./home.component.css']
})
export class HomeComponent {
  constructor() {}
}

home.component.html

<div @slideInOut>
  <button [routerLink]="['', { outlets: {'aux': null} }]">Test</button>
</div>

For some reason, the leave animation doesn't trigger. My component slides in correctly, but not out. It sits for .3s and then disappears. I've tried multiple CSS properties instead of transform and the same thing happens. I've also tried replacing :leave with * => * and * => void just to be sure. Any ideas? Thanks!

Upvotes: 2

Views: 5066

Answers (1)

Mihailo
Mihailo

Reputation: 4917

The reason your :leave animation does not happen is because the route has changed and so did you "canvas"/component.

The :enter trigger animates because your animation is defined within your now active route, and when you visit it the "canvas" is there from the beginning.

Basically you need to think outside the box in our case <router-outlet></router-outlet>

One way you can do it is is to wrap the router-outlet in a div container that will be animated, and the trigger for that animation will be route changes, you can listen for these in a bunch of ways.

Firstly I've added data to our routes to differentiate them:

const routes: Routes = [
  { path: 'home', component: HomeComponent, data: { state: 'home' }  },
  { path: 'about', component: AboutComponent, data: { state: 'about' } },
];

We can then wrap the router-outlet in app.component.html like this:

<main [@routerTransition]="getState(o)">
  <router-outlet #o="outlet"></router-outlet>
</main>

The getState() method in app.component.ts handles the route change detection and triggers the animations:

getState(outlet) {
  // Changing the activatedRouteData.state triggers the animation
  return outlet.activatedRouteData.state;
}

And at the end we have the animation simply attached to the app component's decorator:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  animations: [
    trigger('routerTransition', [
      transition('* <=> *', [    
        query(':enter, :leave', style({ position: 'fixed', width:'100%' })),
        group([ 
          query(':enter', [
            style({ transform: 'translateX(100%)' }),
            animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))
          ]),
          query(':leave', [
            style({ transform: 'translateX(0%)' }),
            animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' }))]),
        ])
      ])
    ])
   ],
})

You can check out this in practice here:

StackBlitz Example

Upvotes: 3

Related Questions