Hello all!
How to determine that the last tab of the application is closed?
Hello all!
How to determine that the last tab of the application is closed?
The VaadinSession provides a list of its related UIs. In the UI’s detach listener, you can check that list and check all UIs closed / attached states.
if (session.getUIs().stream().noneMatch(ui -> !ui.isClosing() && ui.isAttached())) {
// all uis of the session are closed / detached
}
Please note, that there is currently an open issue regarding the reliability of the UI detach event. It seems, that in some browsers, the server is not notified immediately, when a browser tab is closed.
Hi Stefan!
Your recipe doesn’t work!
After close all tabs in my application a see in my logs after onDetach:
VaadinSession.getCurrent()
.getUIs()
.forEach(u -> {
System.out.println("UI " + u.getUIId());
System.out.println("Visible " + u.isVisible());
System.out.println("Enabled " + u.isEnabled());
System.out.println("Closing " + u.isClosing());
System.out.println("Attached " + u.isAttached());
});
UI 4
Visible true
Enabled true
Closing false
Attached true
UI 5
Visible true
Enabled true
Closing false
Attached true
UI 10
Visible true
Enabled true
Closing false
Attached true
UI 11
Visible true
Enabled true
Closing false
Attached true
UI 12
Visible true
Enabled true
Closing true
Attached true
I does work, but it is true, that it might be a bit confusing. As I just recognized, the “is attached” always seem to return true. So what you want to listen for, is only the “closing” flag.
When the detach is called, the “closing” will be true for the one UI, where the tab just got closed. In the next detach event (when closing the next tab), the previous UI will already be removed from the session’s list of UIs.
Here is the simplified variant of your code
@Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addUIInitListener(uiInitEvent -> {
uiInitEvent.getUI().addDetachListener(uiDetachEvent -> {
System.out.println();
System.out.println("DETACH EVENT");
VaadinSession session = uiDetachEvent.getSession();
session.getUIs()
.forEach(u -> {
System.out.println("UI " + u.getUIId());
System.out.println("Closing " + u.isClosing());
});
if (session.getUIs().stream().allMatch(ui -> ui.isClosing())) {
System.out.println("All UIs have been closed");
}
});
});
}
Closing the tabs from first to last opened results in:
DETACH EVENT
UI 0
Closing true
UI 1
Closing false
UI 2
Closing false
DETACH EVENT
UI 1
Closing true
UI 2
Closing false
DETACH EVENT
UI 2
Closing true
All UIs have been closed
Hello, Stefan.
Unfortunately this option also does not work as expected, DetachEvent fired rarely 2 from 5 closed tabs. However c.v.f.s.communication.PushHandler - Connection unexpectedly closed for resource 1bcb8350-caec-438d-b4e9-c1383e55e3ad with transport WEBSOCKET logged correct when i close browser tab.
Google Chrome Version 138.0.7204.169 (Official Build) (64-bit)
As I said initially, there are currently issues with browsers here and there. Unfortunately, the detach event relies on the client sending the event properly when closing the browser tab. Thus if the browser does not properly send the “close” event or the event does not reach the server at all, it will not work.
I tested it locally with my Chrome (138.0.7204.169) on Windows 11, so in theory it should work. Anything beside that might be either system or network dependent or a bug at some other place.
It might make sense to create an issue on your findings with any additional details you have at GitHub · Where software is built