Using Events with Components
Your component class can provide an event-handling API that uses the event bus provided by the Component
base class.
The event bus supports:
-
Event classes that extend
ComponentEvent
, and -
Listeners of the type
ComponentEventListener<EventType>
.
Defining an Event
To use the event bus, your event should extend ComponentEvent
. The base type is parameterized with the type of the component firing the event. This means that the getSource()
method automatically returns the correct component type.
The second constructor parameter determines whether the event is triggered by a DOM event in the browser or through the component’s server-side API.
Example: Creating an event by extending ComponentEvent
.
public class ChangeEvent
extends ComponentEvent<TextField> {
public ChangeEvent(TextField source,
boolean fromClient) {
super(source, fromClient);
}
}
Defining an Event Listener
Event listeners are of the generic ComponentEventListener<EventType>
type, so it is not necessary to create a separate interface for your event type.
In addition, the method for adding a listener returns a handle that can be used to remove the listener, so it is unnecessary to implement a separate method to remove an event listener.
Example: Using the addChangeListener
method to add an event listener.
@Tag("input")
public class TextField extends Component {
public Registration addChangeListener(
ComponentEventListener<ChangeEvent> listener) {
return addListener(ChangeEvent.class, listener);
}
// Other component methods omitted
}
Example: Adding and removing event listeners.
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. Use false
as the second constructor parameter to indicate that the event does not come from the client.
Example: Using the fireEvent
method set to false
to fire an event from the server.
@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 is fired by the element in the browser.
To do this, use the @DomEvent
annotation in your event class to specify the name of the DOM event to listen to. Vaadin Flow automatically adds a DOM event listener to the element when a component event listener is present.
Example: Using the @DomEvent
annotation to connect TextField
component to a DOM event.
@DomEvent("change")
public class ChangeEvent
extends ComponentEvent<TextField> {
public ChangeEvent(TextField source,
boolean fromClient) {
super(source, fromClient);
}
}
Adding Event Data
An event can include additional information, for example the mouse button used for a click event.
The @DomEvent
annotation supports additional constructor parameters. You can use the @EventData
annotation to define which data to send from the browser.
Example: Using the @EventData
annotation to define additional click-event data.
@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 runs as JavaScript in the browser. -
The DOM event is available as
event
and the element to which the listener was added is available aselement
. -
See
DomListenerRegistration.addEventData
in the Javadoc for more about how event data is collected and sent to the server.
Tip
| See Event in the MDN web docs for an overview of standard DOM events and properties. |
Filtering Events
Instead of sending all DOM events to the server, you can filter events by defining a filter
in the @DomEvent
annotation. The filter is typically based on things related to the event.
Example: Defining a filter
in the @DomEvent
annotation.
@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 runs as JavaScript in the browser. -
The DOM event is available as
event
and the element to which the listener was added is available aselement
. -
See
DomListenerRegistration.setFilter
in the Javadoc for more about how the filter is used.
Limiting Event Frequency
Certain kinds of events are fired very frequently when the user interacts with the component. For example, text input events fired while the user types.
You can configure the rate at which events are sent to the server by defining different debounce
settings in the @DomEvent
annotation. Debouncing always requires a timeout
(in milliseconds) and a burst phase
, which determines when events are sent to the server. There are three burst phase options:
-
LEADING
phase: An event is sent at the beginning of a burst, but subsequent events are only sent after one timeout period has passed, without any new events. This is useful for things like button clicks to prevent accidental double submissions. -
INTERMEDIATE
phase: Periodical events are sent while a burst is ongoing. Subsequent events are delayed until one timeout period since the last event has passed. This is useful for things like text input, if you want to react continuously while the user types. -
TRAILING
phase: This phase is triggered at the end of a burst after the timeout period has passed without any further events. This is useful for things like text input if you want to react only when the user stops typing.
Example: Configuring an input
event to be sent to the server half a second after the user’s last input.
@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 active events for several phases at the same time.
Example: Configuring an event for both the LEADING
phase (immediately when a burst starts) and the INTERMEDIATE
phase (while the burst is ongoing).
@DomEvent(value = "input",
debounce = @DebounceSettings(
timeout = 500,
phases = {DebouncePhase.LEADING,
DebouncePhase.INTERMEDIATE }))
public class ContinuousInputEvent
extends ComponentEvent<TextField> {
private String value;
public ContinuousInputEvent(TextField source,
boolean fromClient,
@EventData("element.value") String value) {
super(source, fromClient);
this.value = value;
}
public String getValue() {
return value;
}
}
-
See
DomListenerRegistration.debounce
in the Javadoc for more about debouncing events.
Note
|
If you configure a filter and a debounce rate, only events that pass the filter are considered when determining whether a burst has ended.
|
7E2F75FF-C67E-4477-9A9D-872568AF4216