Wicket in Action

Replace components with animation

21 February 2013, by martin-g

In this article I'm going to show you how to replace a component in Ajax response using animation effects like sliding, fading, etc.

The problem

To update a Component in Ajax request Wicket just replaces its old HTML DOM element with a new one created by the HTML in the response.
To update it with an animation you need to do:

  1. hide the old HTML element
  2. replace the component, but do not show it
  3. show the new HTML element

Attempt 1 (not working):

public void onClick(AjaxRequestTarget target) {
  target.prependJavaScript("jQuery('#"+component.getMarkupId()+"').slideUp(1000);");
  target.add(component);
  target.appendJavaScript("jQuery('#"+component.getMarkupId()+"').slideDown(1000);");
}

The first problem is that animations in JavaScript are implemented with window.setTimeout or with requestAnimationFrame and this makes them asynchronous-ish. That is Wicket will execute the prepend script to hide the element but wont wait for 1000ms before replacing it, so you wont see the slideUp effect.

The second problem is that target.add(component); will show the new HTML element right away and thus slideDown has nothing to do.

The solution

wicket-ajax-jquery.js supports special syntax for the prepended/appended JavaScripts: functionName|some-real-js-code. Such JavaScript is transformed to: function(functionName) {some-real-js-code} where functionName is a function which should be executed in your JavaScript code to allow the next operation to be executed.

Attempt 2 (working):

public void onClick(AjaxRequestTarget target) {
  component.add(new DisplayNoneBehavior());

  target.prependJavaScript("notify|jQuery('#" + component.getMarkupId() + "').slideUp(1000, notify);");
  target.add(component);
  target.appendJavaScript("jQuery('#"+component.getMarkupId()+"').slideDown(1000);");
}
private static class DisplayNoneBehavior extends AttributeAppender {
  private DisplayNoneBehavior() {
    super("style", Model.of("display: none"));
  }

  @Override
  public boolean isTemporary(Component component) {
    return true;
  }
}

Problem 1) is solved by using notify as a callback function which should be executed when the sliding up is finished.
Problem 2) is solved by using DisplayNoneBehavior that add 'display: none' to the markup of this component just for this rendering (see Behavior#isTemporary(Component)).

This code can be extracted in a helper class and reused. Simple implementation of such helper class can be found in the demo application.

This functionality is broken in Wicket 6.4.0, 6.5.0 and 6.6.0. It is fixed with WICKET-5039.

-->