Force continue to next route despite activate() promise


#1

Hi there, I’m hoping there is a simple solution to this. I’m using the activate lifecycle event to gather data from the serverside before rendering the view. Works well, but one problem I have is that in the scenario where a user would click the back button multiple times, it would get stuck waiting on the first route in the queue to resolve its promise. Is there a way to force navigation to the final route destination without getting stuck waiting for a promise to resolve?


#2

It depends a bit on your current implementation but what you can try:

Subscribe to a relevant router event in the activate lifecycle (https://ilikekillnerds.com/2015/09/understanding-aurelia-router-events/ ). Once a new route has been trigger (not sure which event would best describe that intend)- you can abort the current promise (https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort when using fetch)

I’ve used this technique in a slightly more complicated way with great success in another project.


#3

Can I have an example of your implementation? I just tried this by subscribing to router:navigation:processing and the event does not seem to fire until the activate promise is resolved. Kind of puts me in a catch 22. Thank you.

edit: Actually I think it might be more of a lifecycle thing… The event aggregator doesn’t seem to inject itself until after the point of which the event gets fired.

edit 2: Just ran some tests… I set up 3 different routes, let’s call them route1, route2 and route3. At the app level, I set up this code:

    this.eventAggregator.subscribe('router:navigation:processing', (nav) => {
        let navInstruction: NavigationInstruction = nav.instruction;
        let nextRouteName = navInstruction.config.name;
        let lastRouteName = navInstruction.previousInstruction && navInstruction.previousInstruction.config.name || null;
        console.log(`Route change: NEXT ${nextRouteName}, PREVIOUS ${lastRouteName}`);
    });

I started at route1 and navigated normally all the way to route3, then hit the back button two times (putting me back on route1) with an artificial fetch on route2 which is essentially just a promise with a timeout of 3000 inside of it. Here is the console logging as a result of that:

Route change: NEXT route1, PREVIOUS null
Route change: NEXT route2, PREVIOUS route1
Route change: NEXT route3, PREVIOUS route2
Route change: NEXT route2, PREVIOUS route3 <—hit back button 2 times here

You’ll notice that even though I ended up back at route1, the router:navigation:processing event only fired for route2. This leads me to believe that this event is not firing properly when the activate() lifecycle method is awaiting to resolve its promise. I’m not sure how I’m supposed cancel a promise by setting up an EventAggregate subscription on router:navigation:processing if it does not fire while the promise is unresolved. I’m hoping there’s just some oversight on my part as you said you had success with this in the past.


#4

As a quick fix, I added a window.onpopstate listener which will immediately resolve the promise. This will work for me because a user cannot click any links while a view is loading due to style rules. This will catch any cases where the back button is being used, but might not catch normal navigation situations because of this:

Note that just calling history.pushState() or history.replaceState() won’t trigger a popstate event. The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling history.back() or history.forward() in JavaScript).

I’d still like to keep the discussion going on this though, if possible. For obvious reasons it would be a lot better for this functionality to natively plug into the framework.