@Subscribe
private void onInit(InitEvent event) {
}
@Subscribe
private void onBeforeShow(BeforeShowEvent event) {
}
}
Comparing new API with the old approach you can see that we are not overriding hook-methods, which are obscurely called in the hierarchy of parent classes but define logic in clear predefined points of the screen lifecycle.
Event Handling and Functional Delegates
In the previous section we learned how to subscribe to the lifecycle events, so, what about other components? Should we still scatter all required listeners on screen initialization as it was in 6.x versions? The new API is very uniform, so subscribing to other events is absolutely similar to the lifecycle ones.
Let's take a simple example with two UI elements: a button and a currency field, so its XML descriptor looks like:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.company.demo.web">
<layout>
<hbox spacing="true">
<currencyField id="currencyField" currency="$"
currencyLabelPosition="LEFT"/>
<button id="calcPriceBtn" caption="Calculate Price"/>
</hbox>
</layout>
</window>
By clicking the button we call middleware service returning a number, which goes to the currency field. The currency field should change its style depending on the price value.
@UiController("demo_MyFirstScreen")
@UiDescriptor("my-first-screen.xml")
public class MyFirstScreen extends Screen {
@Inject
private PricingService pricingService;
@Inject
private CurrencyField<BigDecimal> currencyField;
@Subscribe("calcPriceBtn")
private void onCalcPriceBtnClick(Button.ClickEvent event) {
currencyField.setValue(pricingService.calculatePrice());
}
@Subscribe("currencyField")
private void onPriceChange(HasValue.ValueChangeEvent<BigDecimal> event) {
BigDecimal price = pricingService.calculatePrice();
currencyField.setStyleName(getStyleNameByPrice(price));
}
private String getStyleNameByPrice(BigDecimal price) {
...
}
}
In the example above we can see two event handlers: one is invoked when the button is clicked and another one gets executed when the currency field changes its value - as simple as that.
Now, let's imagine that we need to validate our price and check that its value is positive. The straightforward way would be to add a validator while screen initialization:
@UiController("demo_MyFirstScreen")
@UiDescriptor("my-first-screen.xml")
public class MyFirstScreen extends Screen {
@Inject
private CurrencyField<BigDecimal> currencyField;
@Subscribe
private void onInit(InitEvent event) {
currencyField.addValidator(value -> {
if (value.compareTo(BigDecimal.ZERO) <= 0)
throw new ValidationException("Price should be greater than zero");
});
}
}
In real world applications a screen entry point usually becomes littered with this kind of screen element initializers. To address this issue CUBA provides the useful annotation @Install
. Let's see how it can help in our case:
@UiController("demo_MyFirstScreen")
@UiDescriptor("my-first-screen.xml")
public class MyFirstScreen extends Screen {
@Inject
private CurrencyField<BigDecimal> currencyField;
@Install(to = "currencyField", subject = "validator")
private void currencyFieldValidator(BigDecimal value) {
if (value.compareTo(BigDecimal.ZERO) <= 0)
throw new ValidationException("Price should be greater than zero");
}
}