Angular router transition animations slide both left and right conditionally

I have managed to get this to work by "faking" the state it is in.

In the component.html:

<div [@animRoutes]="pageState">
   <router-outlet></router-outlet>
</div>

pageState is a variable in the component.ts file.

Whenever I click on a tab which I want to go right, I will set pageState to right, and same for left, and let Angular take over the rest.

Note: You have to create a right and right1 state as a hack, because Angular currently does not support right => right state transitions!! Same applies to left of course.

My @Component annotation is below:

@Component({
    selector: 'app-workspace-container',
    templateUrl: './workspace-container.component.html',
    styleUrls: ['./workspace-container.component.scss'],
    animations: [
        trigger('animRoutes', [
            transition('* => right', right),
            transition('* => left', left),
            transition('* => right1', right),
            transition('* => left1', left),
        ]),
    ],
    changeDetection: ChangeDetectionStrategy.OnPush,
})

Where left, left1, right, right1 are:

const left = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(-100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(100%)' }))], {
            optional: true,
        }),
    ]),
];

const right = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(-100%)' }))], {
            optional: true,
        }),
    ]),
];

TL;DR: Make the state you are going to into a variable, so you can dynamically set the state which you wish you are going to.


Today things are a bit simpler because new animation aliases exist as :increment and :decrement. Aliases have been introduced in Angular 5.

So my modified solution is:

    @Component({
      selector: 'app-workspace-container',
      templateUrl: './workspace-container.component.html',
      styleUrls: ['./workspace-container.component.scss'],
      animations: [
        trigger('animRoutes', [
          transition(':increment', right),
          transition(':decrement', left),
        ]),
      ],
  })
  export class ComponentContainingRouterOutlet implements OnDestroy, OnInit {
    //... ngOnInit,ngOnDestroy

    constructor( private route: ActivatedRoute ) { }

    animationState: number;

    onActivate($event) {
      this.animationState = this.route.firstChild.snapshot.data['routeIdx'];
    }
  }

Call animation at router-outlet position:

<div [@animRoutes]="animationState">
  <router-outlet (activate)="onActivate($event)"></router-outlet>
</div>

modify routes definition as example, look at data: { routeIdx: X } :

    const routes: Routes = [
      {
        path: 'routeOne',
        component: ComponentOne,
        data: { routeIdx: 0 }
      },
      {
        path: 'routeTwo',
        component: ComponentTwo,
        data: { routeIdx: 1}
      },
      {
        path: 'routeThree',
        component: ComponentThree,
        data: { routeIdx: 2 }
      },
      {
        path: 'routeFour',
        component: ComponentFour,
        data: { routeIdx: 3 }
      },
      {
        path: '',
        redirectTo: 'routeOne',
        pathMatch: 'full'
      }
    ]

And transitions are the same as in Dolan's post:

const left = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(-100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(100%)' }))], {
            optional: true,
        }),
    ]),
];

const right = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(-100%)' }))], {
            optional: true,
        }),
    ]),
];