Rolling your own AJAX Behavior with Wicket

Apache Wicket provides a lot of predefined AJAX components like the AjaxButton and the AjaxEventBehavior which fulfill almost any of your AJAX-needs. However, in some special cases you may need to create your own AJAX behavior to pass some custom parameters from client to server via AJAX.

The Problem

The AJAX Behaviors Wicket provides all have a specialized function. The AjaxButton provides an onSubmit-Method to react on a form submission via AJAX. The more general AjaxBehavior provides an onEvent-Method in which you can update parts of your page via AJAX.
But what to do if you have some client-side javascript logic that makes some calculations and you want to send the result of that calculation to your server via AJAX? The following sections provide a surprisingly simple solution for this problem. Scroll to the bottom to see the full 30 lines of the finished behavior class if you want to skip the tutorial part :).

The Behavior

First, we have to create our own AjaxBehavior by subclassing AbstractDefaultAjaxBehavior. The respond-method has to be implemented. This method is called on the server-side when an AJAX call is made from the client. In this method, we want to somehow access some values that are passed from the client-side.
public class MyAjaxBehavior extends AbstractDefaultAjaxBehavior {</p>
  @Override
    protected void respond(AjaxRequestTarget target) {
      // here, we want to access some parameters that were sent
      // by the client via AJAX
    }
   }

Calling the Behavior from Client-Side Javascript

The behavior does nothing so far. It has to be called from the client-side for the respond-method to be called on the server-side. To achieve this, we simply implement the renderHead() to execute some javascript when the page is loaded (you can also add this javascript to any other event of course, like clicking a button).
public class MyAjaxBehavior extends AbstractDefaultAjaxBehavior {</pre>
  @Override
    protected void respond(AjaxRequestTarget target) {
    // here, we want to access some parameters that were sent
    // by the client via AJAX
  }

  @Override
    public void renderHead(Component component, IHeaderResponse response) {
      super.renderHead(component, response);
      response.render(OnDomReadyHeaderItem.forScript(getCallbackScript()));
  }

}
Remember to call super.renderHead(). Otherwise Wicket’s javascript files will not be included. The AbstractDefaultAjaxBehavior class already provides a method that generates the javascript that is needed to issue an AJAX request: getCallbackScript(). This method will generate a javascript fragment something like the following, where “u” and “c” are two Wicket-specific parameters that are needed to find our AjaxBehavior on the server side an call its respond()-method.
Wicket.Ajax.ajax({"u":"./?1-1.IBehaviorListener.2-component","c":"component"});

Passing Parameters from Client to Server

Let’s say we have two custom parameters, param1 and param2, that we want to pass from the client-side javascript to our server-side code. How to do that?

Wicket provides the concept of “extra parameters” that can be added to the javascript AJAX call by adding the “ep” parameter to the function call:

Wicket.Ajax.ajax({
  "u":"./?1-1.IBehaviorListener.2-component",
  "c":"component",
  "ep":[
    {"name":"param1","value":"value1"},
    {"name":"param2","value":"value2"}]});

The Strings “value1” and “value2” will then be submitted via the AJAX call. Our Behavior will have to be altered to generate the extra parameters in it’s javascript output. We do this by overriding the method updateAjaxAttributes():

public class MyAjaxBehavior extends AbstractDefaultAjaxBehavior {</pre>
  @Override
  protected void respond(AjaxRequestTarget target) {
    // here, we want to access some parameters that were sent
    // by the client via AJAX
  }

  @Override
  public void renderHead(Component component, IHeaderResponse response) {
    super.renderHead(component, response);
    response.render(OnDomReadyHeaderItem.forScript(getCallbackScript()));
  }

  @Override
  protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
    super.updateAjaxAttributes(attributes);
    attributes.getExtraParameters().put("param1", "value1");
    attributes.getExtraParameters().put("param2", "value2");
  }

}

However, our Ajax call will now always pass the values “value1” and “value2” for our two parameters. What we want is to pass values dynamically calculated in javascript.

Passing Dynamic Parameter Values

To pass dynamically calculated javascript values to the server, these values have to be calculated first, and then added into our “extra parameters” array.

To dynamically calculate the parameter values, our renderHead-method is altered. Remember: if you want to trigger the AJAX event somewhere else and not onDomReady, you can do so. For the calculation of the javascript values you can call any Javascript module you might use.

  
@Override
  public void renderHead(Component component, IHeaderResponse response) {
    super.renderHead(component, response);
    String script = "var param1Value = My.JavaScript.Module.calculate1();";
    script += "var param2Value = My.JavaScript.Module.calculate2();"; 
    script += getCallbackScript();
    response.render(OnDomReadyHeaderItem.forScript(script));
  }

Next, we have to pass the javascript variables param1Value and param2Value as “extra parameters” in our Wicket.Ajax.ajax function call instead of the fixed values “value1” and “value2”. This can be done by overriding getCallbackScript() and slightly altering our implementation of updateAjaxAttributes() from above:

@Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
  super.updateAjaxAttributes(attributes);
  attributes.getExtraParameters().put("param1", "PLACEHOLDER1");
  attributes.getExtraParameters().put("param2", "PLACEHOLDER2");
}

public CharSequence getCallbackScript() {
  String script = super.getCallbackScript().toString();
  script = script.replace("\"PLACEHOLDER1\"", "param1Value");
  script = script.replace("\"PLACEHOLDER2\"", "param2Value");
  return script;
}

The method updateAjaxAttributes() now inserts placeholders into the extra parameters map. The method getCallbackScript() is overridden to replace all occurrences of these placeholders within quotes with the names of the javascript variables that contain outr calculated values.

The workaround with the placeholders is neccessary because otherwise we will have extra quotes around our javascript variable names (i.e. the value of param1 will always be the string “param1Value” instead of the actual value of the javascript variable with that name).

Accessing the passed Parameters on the Server

At last, we can access the values of the javascript variables on the server within the respond() method of our behavior:
@Override
public CharSequence getCallbackScript() {
  RequestCycle cycle = RequestCycle.get();
  WebRequest webRequest = (WebRequest) cycle.getRequest();
  StringValue param1 = webRequest.getQueryParameters().getParameterValue("param1");
  StringValue param2 = webRequest.getQueryParameters().getParameterValue("param2");
  // do whatever you need with param1 and param2
}

The Complete Behavior

Finally, the whole behavior at a glance:
public class MyAjaxBehavior extends AbstractDefaultAjaxBehavior {

  @Override
  protected void respond(AjaxRequestTarget target) {
    RequestCycle cycle = RequestCycle.get();
    WebRequest webRequest = (WebRequest) cycle.getRequest();
    StringValue param1 = webRequest.getQueryParameters().getParameterValue("param1");
    StringValue param2 = webRequest.getQueryParameters().getParameterValue("param2");
    // do whatever you need with param1 and param2
  }

  @Override
  public CharSequence getCallbackScript() {
    String script = super.getCallbackScript().toString();
    script = script.replace("\"PLACEHOLDER1\"", "param1Value");
    script = script.replace("\"PLACEHOLDER2\"", "param2Value");
    return script;
  }

  @Override
  protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
    super.updateAjaxAttributes(attributes);
    attributes.getExtraParameters().put("param1", "PLACEHOLDER1");
    attributes.getExtraParameters().put("param2", "PLACEHOLDER2");
  }

  @Override
  public void renderHead(Component component, IHeaderResponse response) {
    super.renderHead(component, response);
    String script = "var param1Value = My.JavaScript.Module.calculate1();";
    script += "var param2Value = My.JavaScript.Module.calculate2();";
    script += getCallbackScript();
    response.render(OnDomReadyHeaderItem.forScript(script));
  }

}
Advertisements

3 comments

  1. wicketer

    What if I want to provide the dynamic values later, not immediatelly after the page load? (e.g. when the user has pressed or done something in the page.

    I found it very difficult to adapt the solution for that.. I changed the variable value but nothing has been passed to the wicket side.

  2. Martin Uhlir

    I have the same problem as wicketer. The functions get triggered right after the page has been loaded, but I would need to trigger those only after the user’s action. Any idea how to achieve that?

  3. Martin Uhlir

    I figured that out by myself and the solution was rather simple 😉 Instead of using OnDomReadyHeaderItem I used OnEventHeaderItem.
    Thanks for the great post!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s