UI Events
You can use the Spring’s ApplcationEvent
to respond to events occurred in different parts of the application.
To send application events to UI components including opened views you need to use the UiEventPublisher
bean provided by the framework.
The UiEventPublisher
bean has the following methods:
-
publishEventForCurrentUI()
- publishes event only to the currently active browser tab from which the event was sent. -
publishEvent()
- publishes event to all browser tabs for the current session. -
publishEventForUsers()
- publishes the event to all browser tabs for all sessions of the users specified in theusernames
collection, which is passed as a second parameter. If theusernames
collection isnull
the event will be published for all users.
To correctly update the UI, the class that implements AppShellConfigurator
must contain the @Push
annotation. Usually, this is the main Spring Boot application class:
@Push
@SpringBootApplication
public class OnboardingApplication implements AppShellConfigurator {
Usage Example
Consider the following development task: users need to complete the onboarding process by following some steps. Each step is represented by an entity instance and there is a view that shows information about completed steps. Also, users need a reminder about the number of uncompleted steps, which updates its value every time the steps are updated. For example, a badge in a menu item, so as not to go to a statistics view every time.
Entity instances representing steps can be updated in entity detail views or in the business logic of the application. The menu item badge should be updated whenever an entity related to the current user changes.
The solution to the task above is as follows:
-
Create an event class:
import org.springframework.context.ApplicationEvent; public class OnboardingStatusChangedEvent extends ApplicationEvent { public OnboardingStatusChangedEvent(Object source) { super(source); } }
-
Create an event listener in the view that should be notified. In the example below it’s the main view:
@Subscribe public void onInit(final InitEvent event) { updateOnboardingStatus(); (1) } @EventListener private void onBoardingStatusChanged(OnboardingStatusChangedEvent event) { (2) updateOnboardingStatus(); } private void updateOnboardingStatus() { long number = getUncompletedStepsNumber(); (3) Span badge = null; (4) if (number > 0) { badge = new Span("" + number); badge.getElement().getThemeList().add("badge warning"); } ListMenu.MenuItem menuItem = menu.getMenuItem("MyOnboardingView"); // Can be 'null' if menu item isn't permitted by security if (menuItem != null) { menuItem.setSuffixComponent(badge); } }
1 Update onboarding status the first time a user opens the main view. 2 Annotate a method with the @EventListener
annotation to enable handling of application events in the view.3 Get uncompleted steps number from a service. 4 Set uncompleted steps number value as a badge to the menu item. -
Send the event using the
UiEventPublisher
bean from the EntityChangedEvent listener:@Component public class UserStepEventListener { private final DataManager dataManager; private final UiEventPublisher uiEventPublisher; public UserStepEventListener(DataManager dataManager, UiEventPublisher uiEventPublisher) { this.dataManager = dataManager; this.uiEventPublisher = uiEventPublisher; } @EventListener public void onUserStepChangedBeforeCommit(EntityChangedEvent<UserStep> event) { User user; if (event.getType() != EntityChangedEvent.Type.DELETED) { Id<UserStep> userStepId = event.getEntityId(); UserStep userStep = dataManager.load(userStepId).one(); user = userStep.getUser(); } else { Id<User> userId = event.getChanges().getOldReferenceId("user"); if (userId == null) { throw new IllegalStateException("Cannot get User from deleted UserStep"); } user = dataManager.load(userId).one(); } uiEventPublisher.publishEventForUsers( (1) new OnboardingStatusChangedEvent(this), Collections.singleton(user.getUsername()) ); } }
1 Publish the event to all UIs for all sessions of the user specified in the UserStep
entity that has been changed.