The EventListener annotation is probably going to be the most frequently used new feature (if history from tacos users is any indicator) in Tapestry 4.1. It offers an awful lot, and is based around the functionality now familiar to many in dojo's event API.
See also: EventListener core annotation documentation., Quircksmode
At its core this new annotation allows you to bind client side events to page/component listener methods. "Client Side" events can have a lot of different meanings. It could mean listening to function calls on a Tapestry supported dojo widget, or it could mean listening to changing field values in a Tapestry Form component.
In this example we want to be notified whenever anyone moves their mouse over a particular html element on our page.:
.... <body> <div id="myFavoriteDiv">Big brother is watching you.</div> </body> ....
The java page class snippet required to bind to this event:
.... @EventListener(elements = "myFavoriteDiv", events = "onmouseover") public void watchText() { // do something } ....
That's it! If you'd like more contextual information, like what was happening with the event that initiated the original client-side event just add a BrowserEvent parameter to your listener and it will be automatically populated.
.... @EventListener(elements = "myFavoriteDiv", events = "onmouseover") public void watchText(BrowserEvent event) { // do something System.out.println("User clicked on x/y coordinates " + event.getPageX() + "/" + event.getPageY()); } ....
Depending on the number of parameters you specify you can achieve some fairly complicated (under the covers at least) logic with very little work.
In this example we want our listener method to be called when the Autocomplete component on our page has selected an entry.
The relevant html:
.... <form jwcid="myform@Form" clientValidationEnabled="true"> <fieldset> Select a project: <span jwcid="projectSelect" /> </fieldset> </form> ....
The java page class snippet:
.... @Component(bindings = { "model=projectModel", "value=selectedProject", "displayName=message:choose.project", "filterOnChange=true", "validators=validators:required"}) public abstract Autocompleter getProjectSelection(); @EventListener(targets = "projectChoose", events = "selectOption") public void projectSelected() { // do something } ....
The last example was good for showing how to listen to widget function events, but what are you supposed to do with an event that comes from a IFormComponent that doesn't also submit and update the form value that was changed?
To add automatic submission to our form we only need to modify the definition of our annotation so that it also submits our form named myform before invoking our listener. (asynchronously)
.... @EventListener(targets = "projectChoose", events = "selectOption", submitForm = "myForm") public void projectSelected() { // do something } ....
That's it! When your listener is invoked you can be confident that your projectSelect Autocomplete component has also been updated to reflect the currently selected value.
As an added bonus, form validation is turned off by default with the EventListener annotation as the majority use case is likely to be one off individual events where invoking client side validation would be a cumbersome experience for users.
One common misunderstanding with EventListener is the exact syntax to use when listening to events. The rules aren't very complicated, but there are two important differences: