JefMeyer
JefMeyer

Reputation: 11

Angular 5 Animations - router transition on browser history back

I'm trying to setup a router animation that slides in one direction when the user click on any [routerLink], but when the user triggers a history.back() clicking in a button inside the page or when they click on the browser back button, I want the animation to go on the opposite direction.

This is the solution I got so far.

Main Component Html

<div class="inicio" [@routerTransition]="getState(o)" (@routerTransition.start)="animationStarted($event)" (@routerTransition.done)="animationDone($event)">
    <router-outlet #o="outlet"></router-outlet>
</div>

Main Component TS

getState(outlet: RouterOutlet) {
    let state: number = this.appRouterState.getState(outlet.activatedRouteData.state);
    return state;
}

This is returning state++ when the user goes forward or state-- when the user goes backward

Animation Trigger Setup

return trigger('routerTransition', [
    transition('void => *', [
        query(':enter, :leave', style({ position: 'fixed', width: '100%', height: "100%" }), { optional: true }),
        group([
            query(':enter', [style({ transform: 'translateX(-100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))], { optional: true }),
            query(':leave', [style({ transform: 'translateX(0%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' }))], { optional: true })
        ])
    ]),
    transition(':increment', [
        query(':enter, :leave', style({ position: 'fixed', width: '100%', height: "100%" }), { optional: true }),
        group([
            query(':enter', [style({ transform: 'translateX(-100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))], { optional: true }),
            query(':leave', [style({ transform: 'translateX(0%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' }))], { optional: true })
        ])
    ]),
    transition(':decrement', [
        query(':enter, :leave', style({ position: 'fixed', width: '100%', height: "100%" }), { optional: true }),
        group([
            query(':enter', [style({ transform: 'translateX(100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))], { optional: true }),
            query(':leave', [style({ transform: 'translateX(0%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' }))], { optional: true })
        ])
    ])
]);

State control service

export interface ItemHistorico {
     index: number;
     url: string;
}

@Injectable()
export class AppRouterState {
    private lastIndex: number = 0;
    private isBack: boolean = false;
    private historico: ItemHistorico[] = [];

    constructor(private router: Router, private menuService: MenuService) { }

    public loadRouting(): void {
        this.router.events.filter(event => event instanceof NavigationEnd).subscribe(({ urlAfterRedirects }: NavigationEnd) => {
            if (urlAfterRedirects !== "/") {
                let last: ItemHistorico = _.maxBy(this.historico, h => h.index);
                let max: number = last ? last.index : 1;
                let next = max + 1;

                this.historico = [...this.historico, { index: next, url: urlAfterRedirects }];
                if (!this.isBack) {
                    this.lastIndex++;
                }
                else {
                    this.isBack = false;
                }
            }
            else {
                this.menuService.obterPaginaInicial().subscribe(menuItem => {
                    let url = menuItem.url;
                    this.router.navigate([`/${url}`]);
                });
            }
        });

        window.onpopstate = ev => {
            let hash: string = (ev.currentTarget as Window).location.hash;
            if (hash === "#/") {
                this.isBack = false;
            }
            else {
                this.isBack = true;
                this.lastIndex--;
            }
        };
    }

    public getHistory(): ItemHistorico[] {
        return this.historico;
    }

    public getState(current: string): number {
        return this.lastIndex;
    }

    public getIsBack(): boolean {
        return this.isBack;
    }
}

Obs.: The "void=>*" transition is working and the ":increment" is also working, the problem lies when the user goes backwards, although the state changes and the component loads without problems the animation don't trigger.

Tks for the help.

Upvotes: 0

Views: 1754

Answers (2)

a.fahmiin
a.fahmiin

Reputation: 400

Just a heads up to those who arrived here. The solution does work as of May 2019. Just a few corrections and reminders to be made.

  1. In Main Component Class, this.lastState should be this.lastPage
  2. In you routes file, make sure to add data { state: 'PATH_NAME' }. The code needs this to cross-check on navigation history saved in the service.

Other than that, as long as you've used the correct syntax and added the animations to your router-outlet wrapper div then you should be good to go!

Upvotes: 0

JefMeyer
JefMeyer

Reputation: 11

I manage to resolve the problem. So for anyone who needs a similar solution here goes:

Main Component Template

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

Main Component Class

currentState: number = 0;
lastPage: string;
getState(outlet: RouterOutlet) {
    let state: string = outlet.activatedRouteData.state;

    if (this.lastState !== state) {
        let dir: string = this.appRouterState.getDirection(state);
        if (dir === "f") {
            this.currentState++;
        }
        else {
            this.currentState--;
        }
        this.lastPage= state;
    }

    return this.currentState;
}

History navigation control service

    @Injectable()
export class AppRouterState {
    private history: string[] = [];
    private isBack: boolean;

    constructor(private router: Router) { }

    public loadRouting(): void {
        this.router.events.filter(event => event instanceof NavigationEnd).subscribe(({ urlAfterRedirects }: NavigationEnd) => {
            this.history= [...this.history, urlAfterRedirects ];
        });

        window.onpopstate = ev => {
            this.isBack = true;
        };
    }

    public getHistory(): string[] {
        return this.history;
    }

    public getDirection(page: string): string {
        if (this.isBack) {
            if (`/${page}` === this.history[this.history.length - 3]) {
                this.history.splice(this.history.length - 2, 2);
                this.isBack = false;
                return "b";
            }
        }

        return "f";
    }
}

Animation setup

export function routerTransition() {
    return trigger('routerTransition', [
        transition(':increment', [
            query(':enter, :leave', style({ position: 'fixed', width: '100%', height: "100%" }), { optional: true }),
            group([
                query(':enter', [style({ transform: 'translateX(100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))], { optional: true }),
                query(':leave', [style({ transform: 'translateX(0%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' }))], { optional: true })
            ])
        ]),
        transition(':decrement', [
            query(':enter, :leave', style({ position: 'fixed', width: '100%', height: "100%" }), { optional: true }),
            group([
                query(':enter', [style({ transform: 'translateX(-100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' }))], { optional: true }),
                query(':leave', [style({ transform: 'translateX(0%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(100%)' }))], { optional: true })
            ])
        ])
    ]);
}

Upvotes: 1

Related Questions