Ok, to answer my own question…
(I’m using Vaadin 12 with Spring Boot)
First I create a JavaScript file which adds a beforeUnload event listener to the window and stores if there are any changes.
var hasChanges = false;
function addChangesListener() {
window.addEventListener('beforeunload', function (e) {
if (hasChanges) {
// Cancel the event
e.preventDefault();
// Chrome requires returnValue to be set
e.returnValue = 'There are changes afoot';
}
});
}
function setChanges() { hasChanges = true; }
function resetChanges() { hasChanges = false; }
I then add the @JavaScript import to my UI.
@JavaScript("frontend://js/script.js")
Next I created a ServiceInitListener so I can call the function to add the event listener when the body.onload event if fired.
@Component
public class ApplicationServiceInitListener implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
event.addBootstrapListener(response ->
response.getDocument().body().attr("onload", "addChangesListener();"));
}
}
I then add a valueChangeListener to my binder and get it to call the setChanges function in script.js.
binder.addValueChangeListener(listener -> UI.getCurrent().getPage().executeJavaScript("setChanges();"));
In my ’save’ button, I reset the changes flag in the script.js file…
Button save = new Button("Save",click -> UI.getCurrent().getPage().executeJavaScript("resetChanges();"));
I also changed the ValueChangeMode to EAGER on all the fields in the binder…
binder.getFields().filter(field -> field instanceof HasValueChangeMode)
.forEach(field -> ((HasValueChangeMode) field).setValueChangeMode(ValueChangeMode.EAGER));
That’s it.
Any changes to any of the fields in the binder results in a ‘navigating away’ message from the browser when navigating away or closing the tab or browser.
Good times.
S.