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
BeforeLeaveEvent::postpone() method doesn't prevent to update the browser URL #3619
Comments
The original issue is #3613 |
Criticalness aside, it still an issue that can leave an undesired navigation states visible for the end user. Since the navigation will already cause a new automatic history state to be added, we will anyway have another state entry there. It might just be that this state is actually never loaded in the UI but only in the history state, meaning back/forward navigation can cause confusion and maybe even errors if application developer is not handling this. Maybe the postponed state should actually something that the user can navigate back from? Eg:
b) will cause there to be the original state twice in the history, if the user never does the continuation of the navigation after postpone. After briefly looking, there is no way for deleting history state entries, but you can hack around that. Don't want to take that route. |
Conceptually this is the same issue as closing a dialog: https://github.com/vaadin/vaadin-dialog/issues/56 Am I right in assuming a history state change has to happen in order for Flow to know and notify Can it work like this:
What would be the problems in that approach? |
The challenge is that currently there is a state entry created on client side before the server side round trip, thus when the postpone decision is made on the server side, there has been an additional state already pushed (with the new URL) and we cannot remove that, and replacing it with After looking at the code, it seems that we are currently blocking the browser from pushing a state entry, and instead pushing it ourselves before the change is sent to server. This is done in ScrollPositionHandler. For making it work as Jouni described, we would need to wait for pushing the new state until the server has responded that whether to push the state or not (not when postponed). I'm not sure how much this might break other things. But it looks to me we should investigate and preferably fix this to work so that:
|
Preferably, the old URL should still be retained until the user has confirmed that they want to go to the new URL. Otherwise, things will be slightly weird when hitting refresh in the browser. Things would still be slightly weird since there's no One potential ugly way of achieving this is to do |
It should be possible to just delegate the push state handling to the server side when user has clicked a router link. The server side needs to Need to investigate this. I think also it would be preferable to push the history state on the client side earlier than via execute javascript. Not sure if this is necessary though. |
It would also be desirable that the URL in the browser would be updated immediately when clicking a |
We have a use case that is very similar to this, but with exception that the navigation is done programmatically at server side (UI.navigate). Its a bit confusing that navigation of the view can be prevented but despite that the url updates. |
Currently if I want to use
whenever I call Also if the user does decide to continue with the exit action and I call
I think both should happen automatically when I call Here's the whole logic: @Override
public void afterNavigation(AfterNavigationEvent event) {
// store the current location so that we can restore that in beforeLeave if needed
originalLocation = event.getLocation().getPathWithQueryParameters();
}
@Override
public void beforeLeave(BeforeLeaveEvent event) {
// if user has unsaved changes
if (askBeforeLeave) {
// postpone the view change
ContinueNavigationAction postponedNavigationAction = event.postpone();
// and also replace the top most history state in the browser with this view's location
// https://github.com/vaadin/flow/issues/3619
UI.getCurrent().getPage().executeJavaScript("history.replaceState({},'','" + originalLocation + "');");
// show the user a dialog where they can continue or cancel the exit action
Dialog dialog = new Dialog();
Button cancelButton = new Button("Cancel", VaadinIcon.CLOSE.create(), cancel -> {
dialog.close();
});
Button confirmButton = new Button("Confirm", VaadinIcon.CHECK.create(), confirm -> {
dialog.close();
// change to the view where the user was trying to navigate to
postponedNavigationAction.proceed();
// and also update the address bar to reflect that location
String destination = event.getLocation().getPathWithQueryParameters();
UI.getCurrent().getPage().executeJavaScript("history.replaceState({},'','" + destination + "');");
});
dialog.add(new H1("Are you sure?"), new Paragraph("Discard unsaved changes and leave this view?"),
cancelButton, confirmButton);
dialog.open();
}
} |
So to recap there are three different things to fix here
In cases 1 & 3 also the scroll position needs to be considered as it needs to be retained. |
* Push browser history state after navigation is certain fixes parts of #3619
* Push browser history state after navigation is certain fixes parts of #3619
Shipped in 2.2.0 and to be released in 2.1.10. Still being ported to 3.x versions |
* Push browser history state after navigation is certain fixes parts of #3619
* Push browser history state after navigation is certain fixes parts of #3619
…#8342) (#8359) * Fixing server postpone when used from JavaScriptBootstrapUI. * Cherry pick from 2.2 to master (#8342) * Push browser history state after navigation is certain fixes parts of #3619 * Fix tests. * Push serverConnected when proceed a postponed navigation event. * Add postpone and forward tests. * Use navigateToClient to proceed to a client route. * Handle postpone logic in connectClient. Navigate to client after proceed only on client urls. * Fix test * Ignore CCDM base test class. * Use trimPath to remove ending slash. Replace history if there's a trailing slash in the route. * Add RouterLink test. * Fix sonar. * Fix sonar. * Remove unused test fields.
Let's say you have a view with a router link.
The view is
BeforeLeaveObserver
and has a methodbeforeLeave
which postpones the navigation.The issue is that the browser URL is changed even though the navigation is postponed.
I'm not sure whether it should be fixed or not.
It's confusing since the navigation has not happened.
But on the other hand there is no way to cancel the navigation. It's just postponed for a while and will happen anyway once the
proceed
method is called.So may be it's acceptable to have the url changed even though the navigation has not happened yet.
The reason of the issue is : router link is handled on the client side first in a generic way.
The browser location is changed on the client side via the
com.vaadin.client.flow.RouterLinkHandler
which delegates the call to theScrollPositionHandler.beforeNavigation
method.This method pushes the new location to the browser history immediately.
To be able to fix this particular issue we will need to ask the server-side before do anything on the client side and update the history after the confirmation.
It's quite complicated and is needed only for the
postpone
specific case but theRouterLink
is a generic functionality which doesn't depend anyhow on thepostpone
.Also I'm not sure whether the browser allows to do roundtrip on click at all.
Anyway, I don't think it's critical issue and not sure whether it needs to be fixed at all.
The text was updated successfully, but these errors were encountered: