Use IRequestListener when a component or behavior needs a URL that calls back into it on the server, and none of the built-in links, forms or Ajax behaviors fits. Link itself works this way: its onClick() is called from the listener method onRequest(). For callbacks from JavaScript without a page reload, use an Ajax behavior such as AbstractDefaultAjaxBehavior instead; see Ajax behaviors.
The interface has two methods:
| Method | Purpose |
|---|---|
void onRequest() |
Runs when the callback URL is requested. |
boolean rendersPage() |
Whether Wicket renders the page after onRequest(). Default true. |
Give a component a callback URL
Implement IRequestListener on the component and put the URL from urlForListener() in its markup:
LikeButton.java
public class LikeButton extends WebComponent implements IRequestListener {
public LikeButton(String id, IModel<Integer> likes) {
super(id, likes);
}
@Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
tag.put("href", urlForListener(new PageParameters()));
}
@Override
public void onRequest() {
int likes = (Integer) getDefaultModelObject();
setDefaultModelObject(likes + 1);
}
}
HomePage.html
<a wicket:id="like">Like</a>
When the user follows the link, Wicket finds the page and the component, calls onRequest(), and renders the page again.
Give a behavior a callback URL
A behavior gets its URL from the component it is bound to, with urlForListener(behavior, parameters). This behavior sends the selected value of a <select> to the server when it changes, by adding a request parameter to the callback URL:
ChoiceCallbackBehavior.java
public abstract class ChoiceCallbackBehavior extends Behavior implements IRequestListener {
private Component component;
@Override
public void bind(Component component) {
this.component = component;
component.setOutputMarkupId(true);
}
@Override
public void renderHead(Component component, IHeaderResponse response) {
String url = component.urlForListener(this, new PageParameters()).toString();
response.render(OnDomReadyHeaderItem.forScript(
"document.getElementById('" + component.getMarkupId() + "').addEventListener('change', e => {"
+ " const url = new URL('" + url + "', location.href);"
+ " url.searchParams.set('choice', e.target.value);"
+ " location.href = url; });"));
}
@Override
public void onRequest() {
StringValue choice = component.getRequest()
.getRequestParameters()
.getParameterValue("choice");
onChoice(choice.toOptionalString());
}
protected abstract void onChoice(String choice);
}
Read request parameters with getRequestParameters(), which covers both the query string and posted form data. Values arrive as StringValue; convert them yourself and never trust them, since anyone can send any value to the URL.
Respond with something other than the page
Return false from rendersPage() and schedule your own request handler in onRequest(). This listener answers with JSON:
@Override
public boolean rendersPage() {
return false;
}
@Override
public void onRequest() {
String json = "{\"likes\": " + getDefaultModelObject() + "}";
RequestCycle.get().scheduleRequestHandlerAfterCurrent(
new TextRequestHandler("application/json", "UTF-8", json));
}
The handlers you can schedule are listed in Request processing.
Things to know
- A behavior that implements
IRequestListenermakes its page stateful, because the URL points to one component instance on one page instance. A component that implements it decides withgetStatelessHint();Linkreturnsfalse. On a stateless page, Wicket creates the page again for the callback. See Keep a page stateless. - Wicket calls the listener only when the component is visible and enabled, and the behavior enabled. Otherwise it throws a
ListenerInvocationNotAllowedExceptionand shows the access denied page. OverridecanCallListener()to change this. -
onRequest()runs before the rendering hooks of the request. Change state there and letonConfigure()act on it; see Component lifecycle.