Component events are Tapestry's way of conveying a user's interactions with the web page, such as clicking links and submitting forms, to designated methods in your page and component classes. When a component event is triggered, Tapestry calls the event handler method you've provided, if any, in the containing component's class.
Div | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
|
Let's look at a simple example. Here's a portion of the template for a page (let's call it "Review") that lists documents and lets a user click to edit any one of them.
Code Block | ||||
---|---|---|---|---|
| ||||
<p> Select document to edit: </p> <t:loop source="documents" value="document"> |
...
<div> <t:actionlink" t:id="edit" context="document.id"> ${document.name} </t:actionlink> </div> </t:loop> |
Notice that Review.tml contains an ActionLink component in a loop. For each rendering within the loop, the ActionLink component creates a component event request URL, with the event type set to "action". In this case, each URL might look like:
http://localhost:8080/review.edit/3
This URL identifies the page that contains the component ("review"), the Component id of the component within the page ("edit"), and the context value ("3", the "id" property of the document). Additional context values, if any, are appended to the path. (The URL may also contain the event name, unless, as here, it is "action".)
...
When a component event occurs, Tapestry invokes any event handler methods that you have identified for that event. You can identify your event handler methods via a naming convention (see Method Naming Convention below), or via the @OnEvent annotation.
Code Block | ||
---|---|---|
| ||
@OnEvent(component="edit") void editDocument(int docId) { this.selectedId = docId; // do something with the document here } |
Tapestry does two things here:
...
Since | ||
---|---|---|
| ||
Starting in release 5.3, Tapestry will throw an exception if the component identified for the event handler method doesn't exist in the containing component's template. This helps prevent typos. However, this behavior can be overridden using the @DisableStrictChecks annotation (added in release 5.6.2). |
In the above example, the editDocument() method will be invoked when any event occurs in in the "edit" component (and has at least one context value).
For some components, more than one type of event can occur, in which case you will want to be more specific:
Code Block | ||
---|---|---|
| ||
@OnEvent(value="action", component="edit") void editDocument(int docId) { this.selectedId = docId; // do something with the document here } |
For the OnEvent annotation, the value
attribute identifies the name of the event to match. We specified "action" because the ActionLink component triggers the "action" event, as noted in the Component Events section of its javadocs.
Alternatively, we can use the EventLink component, in which case the name of the event is determined by us – either through the "event" parameter or the element's ID:
Code Block | ||||
---|---|---|---|---|
| ||||
<t:eventlink event="delete" context="document.id"> ${document.name} </t:eventlink> |
which is equivalent to:
Code Block | ||||
---|---|---|---|---|
| ||||
<a t:type="eventlink" t:id="delete" context="document.id"> ${document.name} </a> |
Note that if you omit the component
part of the OnEvent annotation, then you'll receive notifications from all contained components, possibly including nested components (due to event bubbling).
Tip |
---|
You should usually specify exactly which component(s) you wish to receive events from. Using @OnEvent on a method and not specifying a specific component ID means that the method will be invoked for events from any component. |
To support testing, it's a common practice to give event handler methods package-private visibility, as in the examples on this page, although technically they may have any visibility (even private).
...
The previous example may be rewritten as:
Code Block | ||
---|---|---|
| ||
void onActionFromEdit(int docId) { this.selectedId = docId; // do something with the document here } |
Info |
---|
Many people prefer the naming convention approach, reserving the annotation just for situations that don't otherwise fit. |
Method Return Values
Main Article: Page Navigation
...
- Null: For no value, or null, the current page (the page containing the component) will render the response.
- Page: For the name of a page, or a page class or page instance, a render request URL will be constructed and sent to the client as a redirect to that page.
- URL: For a
java.net.URL
, a redirect will be sent to the client. (In Tapestry 5.3.x and earlier, this only works for non-Ajax requests.) - Zone body: In the case of an Ajax request to update a zone, the component event handler will return the new zone body, typically via an injected component or block.
- HttpError: For an HttpError, an error response is sent to the client.
- Link: For a Link, a redirect is sent to the client.
- Stream: For a StreamResponse, a stream of data is sent to the client
- boolean: true prevents the event from bubbling up further; false lets it bubble up. See Event Bubbling, below.
...
The latter two should be avoided, they may be removed in a future release. In all of these cases, the context EventContext parameter acts as a freebie; it doesn't match against a context value as it represents all context values.
Code Block |
---|
Object onActionFromEdit(EventContext context) |
...
{ if (context.getCount() > 0) { this.selectedId = context.get(0); // do something with the document here } else { alertManager.warn("Please select a document."); |
...
return null; } } |
Accessing Request Query Parameters
...
In other words, there's no need to do this:
Code Block | ||
---|---|---|
| ||
void onActionFromRunQuery() { try { dao.executeQuery(); } catch (JDBCException ex) |
...
{ throw new RuntimeException(ex); } } |
Instead, you may simply say:
Code Block | ||
---|---|---|
| ||
void onActionFromRunQuery() throws JDBCException { dao.executeQuery(); } |
Your event handler method may even declare that it "throws Exception" if that is more convenient.
...
When an event handler method throws an exception (checked or runtime), Tapestry gives the component and its containing page a chance to handle the exception, before continuing on to report the exception.{
Div | |||||||||
---|---|---|---|---|---|---|---|---|---|
| |||||||||
|
|background=#eee|padding=0 1em} *JumpStart Demo:* [Handling A Bad Context|http://jumpstart.doublenegative.com.au/jumpstart/examples/infrastructure/handlingabadcontext/1] {float}Tapestry triggers a new event, of type "exception", passing the thrown exception as the context. In fact, the exception is wrapped inside a ComponentEventException, from which you may extract the event type and context.
Thus:
Code Block | ||
---|---|---|
| ||
Object onException(Throwable cause) { message = cause.getMessage(); return this; } |
The return value of the exception event handler replaces the return value of original event handler method. For the typical case (an exception thrown by an "activate" or "action" event), this will be a navigational response such as a page instance or page name.
...
If there is no exception event handler, or the exception event handler returns null (or is void), then the exception will be passed to the RequestExceptionHandler service, which (in the default configuration) will render the exception page.
Triggering Events
Div | |||||||||
---|---|---|---|---|---|---|---|---|---|
| |||||||||
|
triggerEvent
method of ComponentResources from within the your component class.
For example, the following triggers an "updateAll" event. A containing component can then respond to it, if desired, with an "onUpdateAll()" method in its own component class.
Code Block | ||||
---|---|---|---|---|
| ||||
@Inject ComponentResources componentResources; ... private void timeToUpdate() { boolean wasHandled = componentResources.triggerEvent("updateAll", null, null); if (wasHandled) { ... } } |
The third parameter to triggerEvent is a ComponentEventCallback, which you'll only need to implement if you want to get the return value of the handler method. The return value of triggerEvent() says if the event was handled or not.
Scrollbar |
---|