Using Events with Components
Your component class can provide an event handling API using the event bus provided by the Component
base class. The event bus supports event classes extending ComponentEvent
, and the listeners are of the type ComponentEventListener<EventType>
.
Defining an Event
Events used with the event bus should extend ComponentEvent
. The base type is parameterized with the type of the component firing the event, so that getSource()
can automatically return the right component type.
The second constructor parameter is used to let the user know whether the event has been triggered by a DOM event in the browser or through the component’s server-side API.
Java
public class ChangeEvent extends ComponentEvent<TextField> {
public ChangeEvent(TextField source, boolean fromClient) {
super(source, fromClient);
}
}
Event listeners are of the generic type ComponentEventListener<EventType>
, so you don’t need to create a separate interface for your event type.
You don’t need to implement a separate method for removing an event listener. Instead, the method for adding a listener returns a handle that can be used for removing the listener.
Java
@Tag("input")
public class TextField extends Component {
public Registration addChangeListener(
ComponentEventListener<ChangeEvent> listener) {
return addListener(ChangeEvent.class, listener);
}
// Other component methods omitted
}
The user can then add and remove listeners like this.
Java
TextField textField = new TextField();
Registration registration = textField
.addChangeListener(e -> System.out.println("Event fired"));
// In some other part of the code
registration.remove();
Firing Events From the Server
You can fire an event on the server by creating the event instance and passing it to the fireEvent
method. The event should be created with false
as the second constructor parameter to indicate that the event does not come from the client.
Java
@Tag("input")
public class TextField extends Component {
public void setValue(String value) {
getElement().setAttribute("value", value);
fireEvent(new ChangeEvent(this, false));
}
// Other component methods omitted
}
Firing Events From the Client
You can connect a component event to a DOM event that will be fired by the element in the browser. To do this, you only need to tell the framework the name of the DOM event to listen to by adding a @DomEvent
annotation to your event class. The framework will automatically add a DOM listener to the element when a component event listener is added.
Java
@DomEvent("change")
public class ChangeEvent extends ComponentEvent<TextField> {
public ChangeEvent(TextField source, boolean fromClient) {
super(source, fromClient);
}
}
Adding Event Data
An event can also include additional information about what has happened, e.g. which mouse button was used for a click event. If you use @DomEvent
, any additional constructor parameters should have an @EventData
annotation that tells the framework what data to send from the browser.
Java
@DomEvent("click")
public class ClickEvent extends ComponentEvent<NativeButton> {
private final int button;
public ClickEvent(NativeButton source, boolean fromClient,
@EventData("event.button") int button) {
super(source, fromClient);
this.button = button;
}
public int getButton() {
return button;
}
}
The @EventData
definition is run as JavaScript in the browser with the DOM event available as event
and the element to which the listener was added available as element
. See the javadocs for DomListenerRegistration.addEventData
for more information about how event data is collected and sent to the server.
Tip | See https://developer.mozilla.org/en-US/docs/Web/API/Event for an overview of the standard DOM events and their properties. |
Filtering Events
It is sometimes not appropriate to send all DOM events to the server, but instead determine whether to send an event based on something related to the event. To achieve this, you can add define a filter
in the @DomEvent
annotation.
Java
@DomEvent(value = "keypress", filter = "event.key == 'Enter'")
public class EnterPressEvent extends ComponentEvent<TextField> {
public EnterPressEvent(TextField source, boolean fromClient) {
super(source, fromClient);
}
}
The filter
definition is run as JavaScript in the browser with the DOM event available as event
and the element to which the listener was added available as element
. See the docs for DomListenerRegistration.setFilter
for more information about how the filter is used.
Limiting Event Frequency
Some kinds of events are fired very frequently while the user is doing something, like text input events while the user is typing. The rate of sending events to the server can be configured using different debounce
settings in the @DomEvent
annotation. Debouncing always requires a timeout
in milliseconds and one or several phases of a burst for which an event should be sent to the server.
Listening for the
LEADING
phase means that an event will be sent to the server at the beginning of a burst, but subsequent events will be dropped until one timeout period has passed without any new events. This may be useful for things like button clicks if you want to prevent accidental double submissions.With the
INTERMEDIATE
phase, there will be periodical events to the server while a burst is ongoing. Another event will not be sent to the server until the timeout period has passed since the last event was sent. This may be useful for tings like text input if you want to react continuously while the user is typing.The
TRAILING
phase is triggered at the end of a burst, when the timeout period has passed without any further events. This may be useful for things like text input if you want to react only when the user stops typing.
In this example, an input
event will be sent to the server when the user has been typing, but there has been half a second since the last time some input was made.
Java
@DomEvent(value = "input",
debounce = @DebounceSettings(
timeout = 250,
phases = DebouncePhase.TRAILING))
public class InputEvent extends ComponentEvent<TextField> {
private String value;
public InputEvent(TextField source, boolean fromClient,
@EventData("element.value") String value) {
super(source, fromClient);
this.value = value;
}
public String getValue() {
return value;
}
}
You can configure your event to be active for several phases at once, e.g. to both receive an event for the LEADING
phase immediately when a burst starts but also INTERMEDIATE
events while the burst goes on.
Java
@DomEvent(value = "input",
debounce = @DebounceSettings(
timeout = 500,
phases = {DebouncePhase.LEADING,
DebouncePhase.INTERMEDIATE }))
public class ContiniousInputEvent extends ComponentEvent<TextField> {
private String value;
public ContiniousInputEvent(TextField source, boolean fromClient,
@EventData("element.value") String value) {
super(source, fromClient);
this.value = value;
}
public String getValue() {
return value;
}
}
See the docs for DomListenerRegistration.debounce
for more information about how the filter is used.
Note | If both filter and debounce are configured at once, only events that pass the filter are considered when determining whether a burst has ended. |