Handling User Events
Warning
|
Polymer support has been deprecated since Vaadin 18 (released in November 2020), in favor of faster and simpler Lit templates. The built-in support for Polymer templates has been removed and is only available as a commercial add-on. However, a free conversion tool is available to assist in converting Polymer templates to Lit. Read more about setting up the commercial Polymer templates addon in the Upgrade Guide. |
Event handlers aren’t supported for Lit templates. You may use directly, though, component API and DOM events via the Element
API to achieve the same functionality.
Defining Client-Side Event Handlers
PolymerTemplate
has a way to attach client-side event handlers to elements.
Use the on-eventname="methodName"
syntax to define the handler. The method name shouldn’t have any arguments or parentheses.
The example here defines an event handler in the EventHandlerDemo
JavaScript Polymer template:
class EventHandlerDemo extends PolymerElement {
static get template() {
return html`<button on-click="handleClick">Say hello</button>`;
}
static get is() {
return 'event-handler-demo'
}
handleClick() {
console.log('Button was clicked.');
window.alert('Hello');
}
}
customElements.define(EventHandlerDemo.is, EventHandlerDemo);
This defines a <button>
click that displays an alert in the browser.
You can use the on-eventname
syntax to listen to both built-in browser events and custom events (e.g., from a Web Component). You can typically find the available events in the component documentation.
Handling Events on the Server
You can use the @EventHandler
annotation to handle DOM events defined in a Polymer template.
The example here defines the handleClick()
method in a JavaScript Polymer template:
class EventHandler extends PolymerElement {
static get template() {
return html`<button on-click="handleClick">Click me</button>`;
}
static get is() {
return 'event-handler';
}
}
customElements.define(EventHandler.is, EventHandler);
This example is using the @EventHandler
annotation to reference the handleClick()
method:
@Tag("event-handler")
@JsModule("./com/example/event-handler.js")
public class EventHandlerPolymerTemplate extends PolymerTemplate<TemplateModel> {
@EventHandler
private void handleClick() {
System.out.println("Received a handle click event");
}
}
When you add the @EventHandler
annotation on the handleClick()
method, Vaadin automatically connects the handler to the client-side event.
Note
| If you add both client-side and server-side event listeners, the client-side handlers are called before the server-side handlers. |
Adding Event Data to Server-Side Events
An event can include additional information about what occurred, for example the mouse button used for a click event. When you use the @EventHandler
annotation, you can define an @EventData
annotation for each method parameter. This annotation tells Vaadin which data to send from the browser.
Any serializable value present in the client-side event object can be sent. The available data depends on the specific event type.
The next example shows how to define the handleClick()
method in a JavaScript Polymer template:
// same template as for the server-side event handler
static get template() {
return html`<button on-click="handleClick">Click me</button>`;
}
The example here is using the @EventData
annotation to get additional data in the Java template class:
@Tag("event-handler")
@JsModule("./com/example/event-handler.js")
public class EventDataHandlerPolymerTemplate extends PolymerTemplate<TemplateModel> {
@EventHandler
private void handleClick(@EventData("event.altKey") boolean altPressed,
@EventData("event.srcElement.tagName") String tag,
@EventData("event.offsetX") int offsetX,
@EventData("event.offsetY") int offsetY) {
System.out.println("Event alt pressed: " + altPressed);
System.out.println("Event tag: " + tag.toLowerCase(Locale.ENGLISH));
System.out.println("Click position on element: [" + offsetX + ", "+ offsetY +"]");
}
}
The client parses the event data and sends the additional information back to the server for event.type
, event.srcElement.tagName
, and event.offset[X/Y]
.
Note
|
If the EventData can’t be converted to the given format, the server could throw an exception. For example, java.lang.ClassCastException: Cannot cast elemental.json.impl.JreJsonNumber to elemental.json.JsonObject . The client could also throw exceptions if the value given for EventData can’t be executed or converted to JSON.
|
Template Repeater for Model-Specific Event Data
You can use the <dom-repeat>
helper element to get model-specific items as objects in your event handler, provided you use beans to define your template model class. See Using Beans with a PolymerTemplate Model) for more information on this.
This example shows how to create the ModelItemHandlerPolymerTemplate
template and model class:
@Tag("model-item-handler")
@JsModule("./com/example/model-item-handler.js")
public class ModelItemHandlerPolymerTemplate
extends PolymerTemplate<MessagesModel> {
public static class Message {
private String text;
public Message() {
}
public Message(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
public interface MessagesModel extends TemplateModel {
void setMessages(List<Message> messages);
}
@EventHandler
private void handleClick(@ModelItem Message message) {
System.out.println("Received a message: " + message.getText());
}
}
You can now handle click events on the server with the Message
parameter type.
For example, using the <dom-repeat>
(template repeater) in a JavaScript Polymer template would look like this:
class ModelItemHandler extends PolymerElement {
static get template() {
return html`
<dom-repeat items="[[messages]]">
<template>
<div class='msg' on-click="handleClick">[[item.text]]</div>
</template>
</dom-repeat>
`;
}
static get is() {
return 'model-item-handler';
}
}
customElements.define(ModelItemHandler.is, ModelItemHandler);
When the item is clicked, the handleClick()
method is called on the server side and the data is identified by event.model.item
.
Note
|
You can use the @ModelItem annotation with any value provided as a data path. By default, the data path is event.model.item . However, you should declare your data type in some manner via the model definition, so that it can be referenced from the model.
|
Modifying Model Items before Events
The @ModelItem
annotation is only a convenient way of accessing model data. The argument you receive in your event handler callback is the model data from the server side that you can access directly via your model instance. This means that the server doesn’t update the model item on the client in any way. Therefore, if you create a custom event on the client side with data that you want to send to the server as a model item, it’s ignored completely on the server side and the current model data is used instead. You should always keep your model in sync on the server and client by updating it, correctly.
To demonstrate the point clearly, the following example shows the incorrect way to update the model. It shows the UserInfo
model and event handler definition.
public static class UserInfo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface Model extends TemplateModel {
void setUserInfo(UserInfo userInfo);
}
@EventHandler
private void onClick(
@ModelItem("event.detail.userInfo") UserInfo userInfo) {
System.err.println("contact : name = " + userInfo.getName());
}
In the next example, you can see a JavaScript Polymer template that doesn’t update the name of the UserInfo
bean instance:
class ContactHandler extends PolymerElement {
static get template() {
return html`
<input id="name" type="text">
<button on-click="onClick">Send the contact</button>
`;
}
static get is() {
return 'contact-handler';
}
onClick(event) {
this.userInfo.name = this.$.name.value;
event.detail = {
userInfo: this.userInfo,
};
}
}
customElements.define(ContactHandler.is, ContactHandler);
This example results in the server-side model and the client being out of sync because the client-side model isn’t updated correctly. To correctly update sub-properties in Polymer, replace this.userInfo.name = this.$.name.value
with this.set("userInfo.name", this.$.name.value)
. However, in this case, the server-side model is updated automatically for you and there is no need to send this custom event at all.
You can notify the server in some manner about the click event, for example via this.$server
and a @ClientCallable
method. See PolymerTemplate, Binding Model Data for how to get the model value directly from the server-side model.
More Ways to Call the Server
You can call the server in two additional ways. First, you can use the @ClientCallable
annotation. This annotation allows a Java method to be called from client-side code, using the this.$server.serverMethodName(args)
notation. You can use this anywhere in your client-side Polymer class implementation. You can pass your own arguments to the method, as long as the types match the server-side method declaration. See @ClientCallable Annotation for more.
Second, you can also define PropertyChangeListeners
when working with templates. See Enabling Property Changes for more.
Receiving Events after Server Update
Sometimes, you may want to execute client-side logic after a component is updated from the server — during a round trip. For example, the component constructor is called on the client side, but it’s too early to do anything with the component at this stage because the component doesn’t yet have data from the server side.
In these circumstances, you can use the afterServerUpdate()
method. When this method is defined for the component, it’s called each time the component is updated from the server side, allowing you to configure the component with all available data.
The example that follows is using the afterServerUpdate()
method in a JavaScript Polymer template:
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
class MyComponent extends PolymerElement {
static get template() {
return html`
<div>
<div>[[text]]</div>
</div>
`;
}
static get is() {
return 'my-component';
}
afterServerUpdate(){
console.log("The new 'text' value is: " + this.text);
}
}
customElements.define(MyComponent.is, MyComponent);
BD42509B-072E-4235-A966-D3F096E735F4