Listener Methods
Listener methods are the main approach by which you add application-specific behavior to your application.
Listener methods are a kind of call back; when a form is submitted, or a link is clicked. The listener methods exist within your page and component classes. Components such as DirectLink and Form take a listener parameter, and you can use a listener: binding reference to use a listener method in your class as the listener.
A listener method is a public void method. It may take parameters, or may not ... the rules are discussed below. A simple listener method take no parameters, or takes a single parameter of type IRequestCycle. When using the DirectLink component, you may specify additional listener parameters. The listener parameters are encoded into the URL and will be available in a later request, when the listener is triggered.
The listener can gain access these parameters in one of two ways:
- By invoking the getListenerParameters() method of IRequestCycle
- By declaring a method parameter for each listener parameter (in order)
Using the second method is usually the best way. The link parameter values are not simply converted into strings, they are encoded as strings but maintain their type; therefore, the listener method parameters must be of the correct type.
For example, suppose that the link encoded a String objectId and an integer index. The component in the template names the listener method, and the two parameters are passed into the DirectLink as an OGNL list expression:
<a jwcid="@DirectLink" listener="doClick" parameters="{ objectId, index }"> . . . </a>
In the Java class, the listener method might look like:
public void doClick(String objectId, int index) { . . . }
Alternately, the listener method could look like:
public void doClick(IRequestCycle cycle) { Object[] parameters = cycle.getListenerParameters(); String objectId = (String)parameters[0]; int index = ((Integer)parameters[1]).intValue(); . . . }
This second case is maintained in Tapestry 4.0 mostly for backwards compatibility, or to handle the case where a single listener method must handle an indeterminate number of listener parameters.
In fact, Tapestry does a search for the most appropriate method, given the number of listener parameters:
- public void method(parameters)
- public void method(IRequestCycle cycle, parameters)
- public void method()
- public void method(IRequestCycle cycle)
Tapestry 3.0 and earlier only accepted the final variation. Don't get too tricky with multiple overloadings of the method; Tapestry doesn't attempt to match the listener parameter types to the method parameter types (it works just by comparing the number of parameters). However, you can count on Java boxing and autoboxing the parameter values (so you can use int and java.lang.Integer interchangeably).
When creating components that accept a listener as a parameter, you should not invoke the IActionListener directly, instead, you should inject the infrastructure:ListenerInvoker service into your component, and have it invoke the listener. The ListenerInvoker is extensible, and application logic may depend on ListenerInvoker's behavior.
In your component specification:
<parameter name="listener" required="yes" default-binding="listener"/> <inject property="listenerInvoker" object="infrastructure:listenerInvoker"/>
In your source code:
public abstract IActionListener getListener(); public abstract ListenerInvoker getListenerInvoker(); . . . IActionListener listener = getListener(); ListenerInvoker invoker = getListenerInvoker(); invoker.invokeListener(listener, this, cycle);
It is acceptible to pass null as the listener; this saves you the necessity of checking for null when the listener is an optional parameter.