-
Notifications
You must be signed in to change notification settings - Fork 27
event.redirect()
doesn't trigger the navigate
event
#280
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
Comments
Ah, that seems unexpected. I agree that there shouldn't be any limit on the redirect but I'm guessing this restriction might have been a safeguard against multiple different interceptors attempting to redirect since the current design proposal doesn't make the redirect observable from outside the place that called
Navigations could be initiated from outside the router APIs as long as the Angular does have
Would you want to pass the
Though I still have concerns what it looks like to have something outside the SPA router also be participating in the outcome of the navigation, would another possible solution be to have an event that makes the redirect observable to anyone that intercepted the original Footnotes |
I guess what I'm starting to think with MFEs, multiple routers on the page, or multiple individual interceptors of a given |
Allowing redirect() to change the URL multiple times is very reasonable and we can do that. It's a very simple method that, after performing a bunch of checks, just changes
Let me be sure I understand the scenario you are working on. You are trying to write a library, called a router, which has its own But, you are worried about users of your library also having their own (You can try to ensure your Is this understanding correct? If so, I tend to agree that we haven't designed the API in such a way. We've generally assumed there's only one "main" Is supporting this kind of scenario, where there are multiple competing "in control" As Andrew mentions, I'm unsure what a solution to this would look like, even if we did think it was supported. So realistic code would help straighten that out.
Right now, Andrew and I discussed an alternate model, where instead we associate ability-to-commit with the navigation.addEventListener("navigate", e => {
e.intercept({
commit: "after-transition",
handler(controller) {
await delay(1000);
controller.commit();
await delay(2000);
}
})
});
navigation.addEventListener("navigate", e => {
e.intercept({
commit: "after-transition",
handler(controller) {
await delay(2000);
controller.commit();
await delay(500);
}
})
});
navigation.addEventListener("navigate", e => {
e.intercept({
commit: "immediate", // this generates a console warning, and we stay with "after-transition" behavior
handler(controller) {
await delay(5000);
// Attempting to call this would throw an exception:
// this handler has not opted in to being one of the committers.
// controller.commit();
}
})
}); In this scenario, commit would happen at 2000 ms, and finished would happen at 5000 milliseconds. This does make commit and finish more symmetrical, in combining signals from all the different We could do this. It adds complexity, and per the above it's not clear how important supporting this kind of multiple-"in control"- |
We've discussed this at length internally, and there is some alternate proposal that somewhat changes the design of The model is as such:
Handler code can look like this: navigateEvent.intercept({
precommitHandler: async controller => {
if (someCondition) {
controller.redirect({url: "other.html"});
return;
}
if (somethingIsInvalid) {
throw new Error("Abort this navigation");
}
}
handler: async () => { await respond_to_committed_navigation(); }
}); This model allows for some basic coordination between handlers, i.e. what is requested in the OP. The pseudo-code for this looks like this: let precommit_handler_queue = event.pre_commit_handlers;
async function handle_event(event) {
let restart = false;
while (next_precommit_handler = precommit_handler_queue.pop()) {
await next_precommit_handler({redirect: url => {
event.destination.url = url;
restart = true;
});
if (restart) {
precommit_handler_queue = event.pre_commit_handlers;
}
}
commit();
await Promise.all(event.post_commit_handlers);
} |
I apologize for taking so long to reply. Also I'm realizing now that I wasn't clear with my questions, sorry about that 😓. I will provide more code examples in the future to improve.
I now understand that it doesn't make sense for multiple people to call
Currently, it's kind of a nested navigation. I think the code below explains it better.
Sorry I wasn't clear. Redirecting multiple times sounds interesting. In vue router, a redirection immediately restarts all of the navigation guards while the returned promise from router.beforeEach((to, from) => {
console.log("first guard");
if (to.path === "path" && iWanttoRedirect) {
return "/redirected";
}
});
// just for the demo, it only logs
router.beforeEach((to, from) => {
console.log("second guard");
});
await router.push("/somewhere");
// first guard
// first guard
// second guard
router.currentRoute.path; // /redirected In the current implementation, the
I don't expect users to directly call
Thank you for this. Knowing the intended usage helps me a lot to drive the router's API to be in sync with the Navigation API. So I would assume other
I think not. The |
I think using the redirect method would be what you want internally. That’s the way you keep the
The Angular Router works much the same way in this. My first prototyping didn’t bother attempting to map Router redirects to a proper “redirect” in the browser navigation API and just started a fresh one since that somewhat mapped to the router events. But after your comment I thought it might be better to use
Will be curious to hear your thoughts again and thank you for your feedback thus far! We definitely gave the multi-interceptor idea consideration here but I think precommit handler offers some niceties on its own: Some things I like about this approach (mentioned at various times previously but now organized in one place):
A couple potential downsides:
|
This removes the sequential and restarting behavior from them. See related discussion in #280.
The current version of
event.redirect()
only allows to change (once) the final committed URL in an intercepted navigation. In SPA routers, navigation guards can redirect multiple times 1. Each redirection goes through navigation guards and can be redirected again. It's up to the developer to ensure no infinite loops (the router can help detect such cases during development).If
event.redirect()
only allows to change the URL before it is committed, it would not allow routers to intercept redirections created by users and force them to only use router redirections (inside navigation guards) so that the router can create a consistent experience. This is the current situation with the History API, the router has to take over and this limits users to not use the History API directly. In practice this affects any plugin that interacts with History API because it needs to be specific to a framework router. It also affects micro frontends in general because they struggle to coordinate different framework routers (they have to pause them based on the page).Another possible solution is to treat new redirections as a full new navigation2, but this would still mean to discourage users from using
event.redirect()
.Footnotes
At least for Angular and Vue:
https://github.com/WICG/navigation-api/issues/124#issuecomment-2684371423 ↩
The text was updated successfully, but these errors were encountered: