Sunday, January 7, 2007

Backward compatible AJAX development with Wicket

(Also published in Dutch and French, French translation by ZedroS).

Despite the general acceptance of AJAX, there is still some reticence in some sectors. The rich interaction provided by AJAX can not be used by people that, because of their handicap have to use a browser that does not support Javascript or CSS. For a sector like the government it is not acceptable to exclude these people and I believe this is an attitude that should be practiced more often.

Many developers today can or will only create web applications that work solely in Internet Explorer. Building an AJAX application that also works without Javascript is simply too much for many companies. To do this anyway, a number of techniques are known. A more general approach is given by Unobtrusive Javascript, but this article presents a different, more flexible approach that uses Wicket.

Wicket is one of the few fully component based web frameworks. In such a web framework one combines components to larger components until you have a web page. The advantage of components is that it is a lot more comfortable to develop and modify components separately. With a page oriented web framework one must usually develop the whole page simultaneously. Struts for example prescribes that you first collect all information for the whole page before a JSP page will render the (whole) page.

Composing Wicket components (a Wicket introduction)

Once creates a component in Wicket by writing an HTML fragment (the template) and by writing Java code that couples more components to the template. Creation and coupling of components happens during the construction phase. During the render phase the components can add or change the fragment or even completely replace it.

Lets look at an example. Here is an HTML template and the associated Java code:

<h1 wicket:id="title">_Template title</h1>
add(new Label("title", "The real title"));
The Label component is coupled to an h1 element. Label will put the real title in the HTML template during the render phase. The result is:
<h1>The real title</h1>

Composing components is just as simple. Suppose we want to use a title with subtitle on many places. We will create a component for that, a Panel to be precise:

<wicket:panel>
  <h1 wicket:id="title">_Template title</h1>
  <h2 wicket:id="subtitle">_Template subtitle</h2>
</wicket:panel>
class TitlePanel extends Panel {
  public TitlePanel(String id, String title, String subtitle) {
    super(id);
    add(new Label("title", title));
    add(new Label("subtitle", subtitle));
  } }
The panel can now be used (for example in the template of another panel) with:
<span wicket:id="titlepanel"></span>
add(new TitlePanel(
  "titlepanel", "The real title", "with a subtitle"));

Linking between pages is done with the Link component:

<a href="#" wicket:id="detaillink">Book details</a>
add(new Link("detaillink") {
  void onClick() {
    setResponsePage(new DetailPage(bookId));
  } });
The Link component will put a Wicket generated href attribute on the a element during the render phase. When the link is clicked the Wicket servlet will call the onClick method. In this example the response page is changed to a page that is constructed on the spot (pages are of course also components). After this the response page is rendered and sent to the browser. If the onClick method was left empty, the response page would not have changed and the current page is rendered again.

Dynamic pages in Wicket

Links are not only for jumping to other pages. In Wicket it is just as easy to change a part of the page by replacing a component by another one. Lets extend the example a bit:

final BookDetailPanel bookDetailPanel = ...;
add(bookDetailPanel);
add(new Link("detaillink") {
  void onClick() {
    bookDetailPanel.replaceWith(
      new BookDetailPanel(bookId));
  } });
Clicking the link leads to a change in the current page. After this the current page is rendered again, and another book is displayed. Note that exceptionally little code is needed. In many other web frameworks all information of the complete page must be collected again.

The observant reader will have noticed that replacing a piece of a page is a trick that is nowadays mostly done with AJAX. In the example however, we have not used a single line of Javascript. Since the whole page is send to the browser again and again, lets change the example a bit more:

final Component bookDetailPanel = ...;
bookDetailPanel.setOutputMarkupId(true);
add(bookDetailPanel);
add(new AjaxFallbackLink("detaillink") {
  void onClick(AjaxRequestTarget target) {
    Component newBookDetailPanel =
      new BookDetailPanel(bookId);
    newBookDetailPanel.setOutputMarkupId(true);
    bookDetailPanel.replaceWith(newBookDetailPanel);
    if (target != null) {
      target.addComponent(newBookDetailPanel);
    }
  }
});
During the render phase the component AjaxFallbackLink generates both a href and an onclick attribute on the coupled a element. Furthermore it makes sure that a number of Wicket Javascript files are added to the HTML header. Depending on whether Javascript is supported, the browser will request either a normal URL, or it will do an AJAX call. In both cases the Wicket servlet will call the onClick method. In the first case the argument of onClick is null and everything will work exactly as in the previous example. When an AJAX call is done, Wicket will only render the components that were added to the AjaxRequestTarget and the result of that is sent to the browser. On the browser side, the Wicket Javascript searches for the element that needs to be replaced by using the id attribute. To make sure it is set, method setOutputMarkupId(true) is called.

With just a few lines of code we have created an AJAX application that even works in browsers without Javascript.

Conclusion

This article shows only a small piece of Wicket's AJAX capabilities. For example it is fairly easy to let the user know an AJAX call is in progress, to quickly execute some Javascript before an AJAX call (for example to disable a submit button), or to validate a form input when it changed.

Wicket is not only a fantastic framework to easily build modern and maintainable web applications, it is even possible to do it in such a way that older and special browsers can deal with them.

7 comments:

  1. Thanks for a very well written article. Will be a great eye-opener for wicket-curious people - I will send some to read it immediately. :-)

    ReplyDelete
  2. Thanks Per for dropping a note. Writing a blog is so unpersonal, you never know whether your work is appreciated.

    ReplyDelete
  3. Thanks for this brief but conclusive and practical article. I think it should be included in the wicket documentation.
    I am trying to find if wicket is right for a new project, and had found it to be just what I needed.
    Mario

    ReplyDelete
  4. Good article, just what I needed. I am just beginning to dig into Wicket and this helped me.

    ReplyDelete
  5. great article. i wondered how wicket and ajax play together and your example was a great help

    ReplyDelete
  6. I always thought that AJAX was a feature of the web browser. Wicket is injecting JS for AJAX calls, so how could it bypass disabled JS???

    ReplyDelete
  7. @anonymous, it easy to bypass Javascript: just do it on the server!

    ReplyDelete