-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better example and API for rolling back failed navigations #86
Comments
Tricky! At the time navigateerror is sent would we say the navigation is "done" at that point? Or immediately after the navigateerror is resolved? I like event.rollback({navigateInfo}), in general. I think that's ergonomic and a pretty common use case. But I also like the idea of knowing more information about an ongoing navigation. I'm not sure it should be |
The Angular router also has this sort of information tracked in the current navigation. It could be useful if the app history API had similar information available. It's not currently used to determine how to handle rollbacks of rejected navigations but as I mentioned, we don't currently handle rollbacks correctly. There's a proposed change that would enable this and does have to look at the navigation trigger to determine the correct strategy for rolling back: https://github.com/angular/angular/pull/38884/files#diff-03cb1ea13f0b5890eb24ce6fef7a42eed85429da0a90cdb48ab06b58c30cb1c1R1433-R1441. This kind of logic might not be necessary in the app history world if you could |
I think this pushes toward
Again the tricky thing here is replace. The key-based model works fairly poorly with replaces as we've currently specced if (appHistory.previous.key === appHistory.current.key && appHistory.previous.id !== appHistory.current.id) { // see #7
appHistory.navigate(appHistory.previous.url, { state: appHistory.previous.state, replace: true });
} else {
appHistory.goTo(appHistory.previous.key);
} (Note: using post-#84 names.) Again, maybe we could package that up into There are a few ways of making this suck less that I can think of:
I'd very much appreciate thoughts on this since I'm still a little shaky on how to think about when people use |
What about an alternate API approach:
Expose something like this as I think I'm hesitant to just say "let's let people do whatever they want" because I do think that rollback() is 90% of use cases, and the other 10% are probably better reflected as "updates" rather than reversions to some previous state. |
Hmm. I'm trying to think of how that compares with just putting everything on
|
I was bothered by the redundancy too, it could be dropped entirely though. What about:
And then add a rollback method to appHistory:
This should throw an error if the transitionInfo was not the last transition (and no additional transition is currently taking place), or the current transition. We could expose transitionInfo directly on appHistory, or additional on navigationerror/success
|
Not to add too much craziness here without any helpful solution suggestions, but I just want to make sure this scenario is considered when thinking about
What does the rollback do in this scenario? We'd really want to roll all the way back to |
If the navigate to /a is cancelled, it could also be rolled back to /. Then
the navigate to /b would sit on top of / rather than as a replace of /a.
…On Tue, Mar 30, 2021 at 8:02 PM Andrew Scott ***@***.***> wrote:
Not to add too much craziness here without any helpful solution
suggestions, but I just want to make sure this scenario is considered when
thinking about rollback:
1. Application is happily sitting on the homepage /
2. Navigate to /a
3. Framework handles the 'navigate' event using respondWith so it can
run its async guards and such
4. Framework determines it needs to redirect this route so it triggers
a new navigation using replace (this part may change depending on
what's decided for async redirects) to /b
5. Framework handles the new 'navigate' event with respondWith as
well, then runs its guards and determines that the navigation should be
rejected (and rejects the promise).
What does the rollback do in this scenario? We'd really want to roll all
the way back to / rather than /a since we never really allowed /a to
settle/proceed.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#86 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AASEG7W2EKVYWRYM35FQVALTGJYDRANCNFSM4ZZ757YA>
.
|
Here's my variant of this, merging a bit with your previous proposal: // can be null if no transition is ongoing
appHistory.transition;
// "replace", "push", or "go" (maybe "traversal" instead of "go"?)
appHistory.transition.type;
// timestamp
appHistory.transition.initiated;
// AppHistoryEntry
// maybe "source" instead of "previous"?
appHistory.transition.previous;
// Only works while the transition is active, i.e.
// you cannot save `const oldTransition = appHistory.transition` and call `oldTransition.rollback()`
// unless `appHistory.transition === oldTransition`.
appHistory.transition.rollback({ navigateInfo }); The transition gets cleared after
Great to make this concrete. As Tom says, I think this would work best if you rolled back both transitions. With the above API, that'd look something like: appHistory.addEventListener("navigate", e => {
e.respondWith((async () => {
const result = await allowedToGoTo(e.destination.url);
if (result.type == "redirect") {
appHistory.transition.rollback();
appHistory.navigate(result.destinationURL, { state: result.destinationState });
} else if (result.type === "disallow") {
throw new Error("Bad!");
}
})());
});
appHistory.addEventListener("navigateerror", e => {
appHistory.transition.rollback();
}); I think this works... |
+1 that looks good to me! |
This gives insight into any "ongoing" navigation, i.e. a navigation that hasn't yet reached navigationsuccess/navigationerror because the promise passed to respondWith() has not yet settled. Additionally: * Removes the finished property from every AppHistoryEntry, which is nice because it only ever really made sense on the current entry; it was about the transition. Instead, the presence or nullness of appHistory.transition can be used. * Adds a type property to AppHistoryNavigateEvent since we're going to have it on appHistory.transition anyway so having it earlier (before respondWith() is called) seems very reasonable. Closes #86. Closes #11 by adding the appHistory.transition.finished promise. Helps with #41 as you can retrieve data from the replaced entry, at least during the transition, with appHistory.transition.previous. Probably helps with #14 (although currentchange needs a bit of an overhaul) since appHistory.transition has the sort of useful information discussed there, such as the previous entry or the type of navigation.
This gives insight into any "ongoing" navigation, i.e. a navigation that hasn't yet reached navigationsuccess/navigationerror because the promise passed to respondWith() has not yet settled. Additionally: * Removes the finished property from every AppHistoryEntry, which is nice because it only ever really made sense on the current entry; it was about the transition. Instead, the presence or nullness of appHistory.transition can be used. * Adds a type property to AppHistoryNavigateEvent since we're going to have it on appHistory.transition anyway so having it earlier (before respondWith() is called) seems very reasonable. Closes #86. Closes #11 by adding the appHistory.transition.finished promise. Helps with #41 as you can retrieve data from the replaced entry, at least during the transition, with appHistory.transition.from. Probably helps with #14 (although currentchange needs a bit of an overhaul) since appHistory.transition has the sort of useful information discussed there, such as the previous entry or the type of navigation.
In an offline discussion @atscott pointed out that the rollback example at https://github.com/WICG/app-history#example-handling-failed-navigations does the wrong thing in any cases besides a simple non-replace forward navigation. Some cases to consider would be:
navigateerror
event (e.g., after logging out, trying to go back to a login-guarded page, which the router rejects). Then, the correct way of rolling back the navigation is actually to doappHistory.back()
appHistory.goTo()
with a saved key.Also, he mentioned that such rollbacks likely want to communicate to the router that they are a rollback, so it can treat them specially. This is likely best done via a
navigateInfo
parameter.There are a few ways one could envision solving this. So far I've come up with:
event.previous
AppHistoryEntry
, to thenavigateerror
event. (And to thenavigatesuccess
event?) Then rollbacks would be somewhat manual, e.g.await appHistory.goTo(event.previous.key, { navigateInfo })
.event.previous.key === appHistory.current.key
, even thoughevent.previous.id !== appHistory.current.id
). But it'd need its own special-case coding, and it'd be easy to forget.event.rollback({ navigateInfo })
to thenavigateerror
event.event.rollback()
is explained as a helper function that does some of the trickier rollback logic for you, but you can always construct your own.navigateerror
event, e.g. we could just always haveappHistory.previous
available (which might also help with Retrieving data from replaced entries #41).Any thoughts, or other ideas?
The text was updated successfully, but these errors were encountered: