Issue
Is it possible to run route animations conditionally in Angular 14?
I have an Angular PWA with route animations. It works great, however I have a slight issue on iOS Safari. There's a feature in this browser which lets the user swipe back and forth with gestures. I actually like this feature so I do not want to disable it. The problem is that when the user swipes back and forth my route animations becomes glitchy.
So ideally I only want to run my route animations if the navigation was triggered imperatively (e.g. click event).
This is what I have so far. With prepareRoute
being async no animations run whatsoever.
How could I work around this problem? Any alternative solutions?
<main [@routeAnimations]="prepareRoute(outlet)">
<router-outlet #outlet="outlet"></router-outlet>
</main>
...
public async prepareRoute(outlet: RouterOutlet) {
const navigation = await lastValueFrom(this.router.events.pipe(filter(event => event instanceof NavigationStart))) as NavigationStart;
// Only run route animation if navigation was imperative (e.g. click)
if (navigation.navigationTrigger === 'imperative') {
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
}
}
Solution
Tricky, but you can.
What you have to do :
- Listen to router events (NavigationEnd)
- When an event happens, check the router state for a boolean
- if the boolean is there, update your animation trigger
- add a state to all of your router links
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
animations: [
trigger('animate', [
transition('* <=> *', [
query(':enter', style({ opacity: 0 }), { optional: true }),
query(':leave', animate('500ms', style({ opacity: 0 })), {
optional: true,
}),
query(':leave', style({ opacity: 0, position: 'absolute' }), {
optional: true,
}),
query(':enter', animate('500ms', style({ opacity: 1 })), {
optional: true,
}),
]),
]),
],
})
export class AppComponent {
@ViewChild(RouterOutlet, { static: true }) ro: RouterOutlet;
name = 'Angular ' + VERSION.major;
animationTrigger: string;
get routeState() {
return this.router.getCurrentNavigation()?.extras?.state;
}
constructor(
private router: Router,
private children: ChildrenOutletContexts
) {
router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe((event) => {
const animate =
this.router.getCurrentNavigation()?.extras?.state?.animate;
if (animate)
// Update the trigger to trigger the animation
this.animationTrigger = this.ro.activatedRoute.snapshot.data.page;
});
timer(5000, 5000).subscribe(() => {
const url = this.router.url;
const dest = url === '/hello' ? '/goodbye' : '/hello';
// Don't send state here = no animation
this.router.navigateByUrl(dest);
});
}
ngOnInit() {}
}
<button routerLink="hello" [state]="{ animate: true }">Hello</button> <br />
<button routerLink="goodbye" [state]="{ animate: true }">Goodbye</button>
<br />
animationTrigger : {{ animationTrigger || 'none' }}
<div [@animate]="animationTrigger">
<router-outlet></router-outlet>
</div>
Answered By - MGX
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.