Wicket in Action

Wicket 6 JavaScript improvements

05 July 2012, by martin-g

A major rework of the internals of the core Wicket JavaScript libraries has been done for Wicket 6. The good news is the client APIs are mostly the same and the application developers will not need to change their applications.

Event registration

Until version 1.5.x attaching an Ajax event behavior to a component would lead to addition of an inline attribute to the markup of this component. For example the produced markup for an AjaxLink is similar to:

<a id="linkId" onclick="wicketAjaxGet('some/url')">Link</a>

In Wicket 6 JavaScript event registration is being used instead. For example the above becomes:

<head>
  ...
  <script>
    Wicket.Event.add(window, 'domready', function(event) {
      Wicket.Ajax.get({'u': 'some/url', 'c': 'linkId', 'e': 'click'}));
      // ... more event registrations and onDomReady scripts
    });
  </script>
</head>
...
<a id="linkId">Link</a>

The benefit is that the event registrations for all elements are done in one place - the page header and your markup elements are not cluttered with extra attributes. Also the generated code for the event registration is much shorter than the one produced in the earlier versions.

jQuery as backing library

The previous implementations of wicket-ajax.js and wicket-event.js were home baked solutions which worked well but also suffered from the differences in the browsers. Often users complained that some functionality doesn't work on particular version of particular browser. That's why the Wicket team chose to use JQuery to deal with browser inconsistencies and leave us to do our business logic.

All Java components and behaviors still use the Wicket.** APIs so if an application already uses another big JavaScript library and adding jQuery is not an option then the developer of this application will be able to provide implementation of wicket-event.js and wicket-ajax.js based on her favorite JavaScript library.
To do it the developer should use the new IJavaScriptLibrarySettings:

MyApplication#init():

public void init() {
  super.init();

  IJavaScriptLibrarySettings jsSettings = getJavaScriptLibrarySettings();

  jsSettings.setBackingLibraryReference(new DojoResourceReference());

  jsSettings.setWicketEventReference(new DojoWicketEventResourceReference());

  jsSettings.setWicketAjaxReference(new DojoWicketAjaxResourceReference());

}

Ajax request attributes

The main change in the Java code is the introduction of AjaxRequestAttributes in Ajax behaviors API.
AjaxRequestAttributes can be used to configure how exactly the Ajax call should be executed and how its response should be processed. To do this use:

AnyAjaxComponent/AnyAjaxBehavior.java:

  @Override
  protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
  {
      super.updateAjaxAttributes(attributes);

      attributes.setSomeAttribute();
  }

This way the developer can specify whether the Ajax call should use GET or POST method, whether to submit a form, on what JavaScript events to listen to, etc. For a full list of the available attributes see the new Wiki page. Most of the attributes have default values and they may be omitted in the generated JavaScript code.

Ajax call listeners

Because of the usage of event registration there is no need of IAjaxCallDecorator because there is no script to decorate anymore. The same functionality can be achieved with the new IAjaxCallListener. It provides the means to register JavaScript listeners which are being called during the Ajax call execution. A listener may provide a precondition which may cancel completely the Ajax call when evaluated to false, a before handler - executed right after the successful pass of the preconditions, an after handler - executed right after the make of the Ajax call, a success handler - executed on successful return of the Ajax call, a failure handler - executed when the Ajax call fails for some reason, and a complete handler which is executed after the success/failure handlers no matter what.

To register an AjaxCallListener do something like:

  @Override
  protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
  {
      super.updateAjaxAttributes(AjaxRequestAttributes attributes);

      IAjaxCallListener listener = new IAjaxCallListener()
      {
        @Override
        public CharSequence getBeforeHandler(Component c) { return handler; }
        .....
      };

      attributes.getAjaxCallListeners().add(listener);
  }

In the example above the returned handler could be any instance of CharSequence. If it is an instance of JsonFunction then it will be written as is, otherwise Wicket will assume that the text is just the body of a JavaScript function and will wrap it in JsonFunction for you. The benefit of using JsonFunction is that it generates JSON with function as a value. This is non-valid JSON but this way there is no need to use eval() in the browser and the execution of the handlers is faster.

Global Ajax call listeners

IAjaxCallListener's can be used to listen for the lifecycle of an Ajax call for a specific component. If the user application needs to listen for all Ajax calls then it may subscribe to the following topics:

  • /ajax/call/before
  • /ajax/call/after
  • /ajax/call/success
  • /ajax/call/failure
  • /ajax/call/complete

Those replaces the old Wicket.Ajax.(registerPreCallHandler|registerPostCallHandler|registerFailureHandler) methods and uses publish/subscribe mechanism.

Example (JavaScript):

    Wicket.Event.subscribe('/ajax/call/failure', function(jqEvent, attributes, jqXHR, errorThrown, textStatus) {
      // do something when an Ajax call fails
    });

All global listeners receive the same arguments as the respective IAjaxCallListener handler plus jQuery.Event that is passed by the PubSub system.

Demo application

At Martin Grigorov's GitHub repository you may find a simple demo application that shows some of the new features.
The application provides a custom AjaxButton (HandlebarsButton) that uses:

  • org.apache.wicket.ajax.attributes.AjaxRequestAttributes#setWicketAjaxResponse(false) - tells Wicket.Ajax to not try to process the returned Ajax response. The response is custom JSON which will be processed by a custom success handler
  • org.apache.wicket.ajax.attributes.AjaxRequestAttributes#setDataType("json") - a hint to Wicket.Ajax that the response is JSON and it will try to parse it for us, so your success handler can use it as JavaScript object directly
  • usage of JsonFunction as success handler - this way the application needs to provide the complete code of a JavaScript function that will handle the success case
  • usage of plain Strings with the function bodies for the precondition, before and complete handlers - Wicket will wrap them in JsonFunction for you
  • shows how to suppress the execution of AjaxRequestHandler - there is no need of it since we return custom JSON response

The application itself just updates a POJO (an article) at the server side and serializes it to JSON which is used at the client side to be rendered with the help of Handlebars JavaScript library.

More

More complete documentation may be found at the Wiki. Feel free to contribute to make it better. Just create an account and edit it!