View Events

This section describes the view lifecycle events that can be handled in controllers. To gain a general understanding of the sequence of events, see the diagrams at the end of the section.

To generate a view event handler stub in Jmix Studio, select the root view element in the Jmix UI structure panel and use the Handlers tab of the inspector panel.

Alternatively, execute the Generate Handler action available in the top panel of the view class and through the CodeGenerate menu (Alt+Insert / Cmd+N).

InitEvent

InitEvent is the first event in the view opening process. The view and all its declaratively defined components are created, and dependency injection is completed. Some visual components are not fully initialized, for example, buttons are not linked with actions. In this event listener, you can create and/or initialize visual and data components.

@ViewComponent
private JmixComboBox<String> timeZoneField;

@Subscribe
public void onInit(final InitEvent event) {
    timeZoneField.setItems(List.of(TimeZone.getAvailableIDs()));
}
The event is triggered only once after the view is created. This means that if the navigation to the view occurs for the first time, the event triggers. However, if navigation is performed to a view that is currently open, for example, by clicking on the same menu item multiple times, the event will not be triggered because the view instance has already been created.

BeforeShowEvent

BeforeShowEvent is the second event (after View.InitEvent) in the view opening process. All components have completed their internal initialization procedures. Data loaders have been triggered by the automatically configured DataLoadCoordinator facet. Security policies are applied to UI components. In this event listener, you can load data, check permissions and modify UI components. For example:

@ViewComponent
private CollectionLoader<User> usersDl;

@Subscribe
public void onBeforeShow(final BeforeShowEvent event) {
    usersDl.load();
}

Consequent navigation to a view that is currently open, leads to triggering BeforeShowEvent again for the same instance of the view.

For example, the user navigates to a view for the first time, the view instance is created and BeforeShowEvent is triggered. Then, the user clicks on the same menu item, navigating to the same opened view again, and BeforeShowEvent is triggered again for the same view instance. If the BeforeShowEvent listener adds components or loads data, it will be done twice, which could lead to duplicating components or reloading data.

BeforeShowEvent works this way, because in case of navigation, it is fired by Vaadin’s BeforeEnterEvent lifecycle event handler.

ReadyEvent

ReadyEvent is the last event (after View.BeforeShowEvent) in the view opening process. In this event listener, you can make final configuration of the view according to loaded data and show notifications or dialogs.

@Autowired
private Notifications notifications;

@Subscribe
public void onReady(final ReadyEvent event) {
    notifications.show("Just opened");
}

Consequent navigation to a view that is currently open, leads to triggering View.ReadyEvent again for the same instance of the view.

For example, the user navigates to a view for the first time, the view instance is created and View.ReadyEvent is triggered. Then, the user clicks on the same menu item, navigating to the same opened view again, and View.ReadyEvent is triggered again for the same view instance. If the View.ReadyEvent listener adds components or loads data, it will be done twice, which could lead to duplicating components or reloading data.

ReadyEvent works this way, because in case of navigation, it is fired by Vaadin’s AfterNavigationEvent lifecycle event handler.

AttachEvent

AttachEvent is fired after a view is attached to the UI.

@Subscribe
public void onAttachEvent(final AttachEvent event) {
    log.debug("View is attached");
}
This event is fired by Vaadin, since a view is a UI component too.

BeforeCloseEvent

BeforeCloseEvent is the first event in the view closing process. The view is still displayed and fully functional. In this event listener, you can check any conditions and prevent closing using the preventClose() method of the event.

@Subscribe
public void onBeforeClose(BeforeCloseEvent event) {
    if (!isLicenseAgreementAccepted()) {
        CloseAction action = event.getCloseAction();
        if (action instanceof NavigateCloseAction navigateCloseAction) {
            BeforeLeaveEvent beforeLeaveEvent = navigateCloseAction.getBeforeLeaveEvent();
            beforeLeaveEvent.postpone();
        }

        event.preventClose();
    }
}
If you prevent closing the view, in case of navigation you need to postpone it too. Check the type of action and if it’s NavigateCloseAction, then obtain Vaadin’s BeforeLeaveEvent and call its postpone() method.

AfterCloseEvent

AfterCloseEvent is the second event (after View.BeforeCloseEvent) in the view closing process. In this event listener, you can show notifications or dialogs after closing the view, for example:

@Autowired
private Notifications notifications;

@Subscribe
public void onAfterClose(final AfterCloseEvent event) {
    notifications.show("View is closed");
}

DetachEvent

DetachEvent is fired before a view is detached from the UI. This event listener can be used for releasing resources acquired by the view.

@Subscribe
public void onDetachEvent(final DetachEvent event) {
    log.debug("View is detached");
}
This event is fired by Vaadin, since a view is a UI component too.

QueryParametersChangeEvent

QueryParametersChangeEvent informs which query parameters a view was opened with. It is sent after InitEvent if the view was opened by navigation.

@ViewComponent
private Span messageLabel;

public void setMessage(String message) {
    messageLabel.setText(message);
}

@Subscribe
public void onQueryParametersChange(final QueryParametersChangeEvent event) {
    event.getQueryParameters()
            .getSingleParameter("message")
            .ifPresent(this::setMessage);

}

InitEntityEvent

InitEntityEvent is fired in views inherited from StandardDetailView before the new entity instance is set to the edited entity container.

Use this event listener to initialize default values in the new entity instance, for example:

@Subscribe
public void onInitEntity(final InitEntityEvent<User> event) {
    User user = event.getEntity();
    user.setOnboardingStatus(OnboardingStatus.NOT_STARTED);
}

ValidationEvent

ValidationEvent is fired in views inherited from StandardDetailView when the view is validated on saving the view DataContext. Use this event listener to perform additional validation of the view.

@Subscribe
public void onValidation(final ValidationEvent event) {
    if (entityStates.isNew(getEditedEntity())
            && !Objects.equals(passwordField.getValue(), confirmPasswordField.getValue())) {
        event.getErrors().add("Passwords do not match");
    }
}

BeforeSaveEvent

BeforeSaveEvent is fired in views inherited from StandardDetailView before saving the view DataContext. Use this event listener to prevent saving, alter entity attribute values before saving or/and interact with the user before save.

@ViewComponent
private JmixPasswordField passwordField;
@ViewComponent
private JmixPasswordField confirmPasswordField;

@Autowired
private EntityStates entityStates;
@Autowired
private PasswordEncoder passwordEncoder;

@Subscribe
public void onBeforeSave(final BeforeSaveEvent event) {
    if (entityStates.isNew(getEditedEntity())) {
        if (!Objects.equals(passwordField.getValue(), confirmPasswordField.getValue())) {
            notifications.create("Passwords do not match")
                    .withType(Notifications.Type.WARNING)
                    .show();
            event.preventSave(); (1)
        }

        getEditedEntity().setPassword(passwordEncoder.encode(passwordField.getValue())); (2)
    }
}
1 Interrupt the saving process if passwords do not match.
2 Set encoded password to the entity.

AfterSaveEvent

AfterSaveEvent is fired in views inherited from StandardDetailView after saving the view DataContext. Use this event listener to notify users after save, for example:

@Autowired
private Notifications notifications;

@Subscribe
public void onAfterSave(final AfterSaveEvent event) {
    notifications.create("Entity saved successfully")
            .withType(Notifications.Type.SUCCESS)
            .withPosition(Notification.Position.TOP_END)
            .show();
}

Diagrams

Opening Standard View

The following diagram shows the process of opening a standard view:

open standard view

Closing Standard View

The following diagram shows the process of closing a standard view:

close standard view

Opening Entity Detail View

The following diagram shows the process of opening a detail view:

open detail view

Closing Entity Detail View

The following diagram shows the process of saving changes and closing a detail view by the detail_saveClose action:

detail view close with save