Documentation

Documentation versions (currently viewingVaadin 24)

Polymer support is deprecated. Use Lit templates instead.

Handling User Events

How to use component API and DOM events via the Element API.
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