WorldTurner Blog

Friday Aug 20, 2010

Wicket best practices: Components vs Pages

Intro

In my last few projects I have used Wicket extensively. I have used Spring WebMVC, JSF, Struts and plain JSPs in the past, as well as XSLT templates and pages based mostly on Javascript and remoted through DWR. And a few others I forgot.

I'm quite happy with Wicket. It isn't a cutting-edge Web 2.0 platform, but it gives me an easy and fast way to make maintainable applications with good and complex web-based GUIs, that can be extended to use client-side interaction when necessary.

I find it quite a bit easier to use than JSF. And once you get to know it, it is just as fast, even for simple GUI's, as Spring WebMVC (Wicket has a steeper learning curve though, in my view). And for complex GUI's, it is no contest: Wicket wins hands-down.

Abstract

Although I like Wicket, I have found it hard to find information about the best way to build applications with Wicket. Being a powerful framework, Wicket has more than one way to do things. They all have their advantages and disadvantages, and it would be good to know these up-front. Secondly, if you're going to develop Wicket applications as a team, you're going to need some guidelines and examples to make sure that everyone is 'reading from the same book'.

And such information is hard to come by.

I'm going to write a few blog entries about the approaches and strategies that worked for us.

Components versus Pages

Wicket has two kinds of building blocks for your applications: Components and Pages. Pages are top-level components, they exist by themselves and cannot be used as part of other Pages or Components. Components can be put on Pages or placed inside other Components. When you're developing using Wicket, you have to choose to make your own classes extend Page or Component.

Apart from the above, Pages and Components offer mostly the same features.

Two notes:

  • a Page is a kind of Component in Wicket, but it is handled differently and cannot be added to another Page or Component.

  • Using frames or iframes it is possible to nest a Page inside a Page visually in the browser, but this does not offer the same flexibility as 'physically' nesting a Component.

You need Pages. Period. Pages can be accessed from a browser, while Components can only be accessed once they are added, directly or through a parent Component, to a Page. Compared to a desktop GUI framework like Swing, a Wicket Page is like a JFrame or JWindow. You need them to start displaying something to the user.

Page-based programming

It turns out that the majority of people, when they write their first Wicket applications, use Page as super-class for the code that they develop.

The reasons for this vary, two of them are:

  • many other web frameworks are based on pages, so this seems logical for someone starting out with Wicket

  • “Wicket in Action”, one of the most popular books about Wicket, uses page-based programming in its examples

Advantages:

  • you can make a bookmark-able link to a Page (most other links in Wicket are transient and will not work after the user session has expired)

  • you need a Page to show something to the user, so it saves code if you put everything on the Page instead of creating both a Page and a Component

Disadvantages:

  • Pages are less re-usable and flexible

Component-based programming

People who start out writing Components (mostly) usually have experience with a component-based desktop GUI framework, and have understood the paradigm change that Wicket offers: developing web applications in a component-oriented way, instead of an HTML-page oriented way.

Advantages:

  • Components are re-usable and flexible

Disadvantages:

  • You can't make bookmark-able links to Components (at least not with Wicket 'out of the box')
  • Components need to be added to Pages before they can be shown

Re-usability and flexibility may be nice, but if you're not going to need it: why bother? That's a YAGNI. (You Ain't Gonna Need It).

I hope that I can convince you that you are going to need it, and that it is possible to address the two disadvantages with some utility code.

When flexibility is useful and needed

Swapping Components but staying on the same Page

One of the good things that the flexibility of Components brings you is not always clear to developers who are just starting with Wicket. You can replace one Component with another Component while staying on the same Wicket Page. To do that, you just swap out one Component for another Component.

This requires an http request from the browser (unless you are using Ajax support) but conceptually, for your Wicket code, you are staying on the same Page, and you don't need to do anything to maintain the state of all the other Components on the Page.

public class EditPanel extends Panel {
    /* ... */
    public void init() {
        add(new Link<Void>("overview") {
            @Override
            public void onClick() {
                EditPanel.this.replaceWith(new OverviewPanel(getId()));
            }
        });
    }
}

Example: swapping out and edit panel for an overview panel in a CRUD interface


Building a wizard with multiple steps is trivial. Showing some detail information in a floating div on the right side, depending on what you clicked, is very easy. A simple administrative CRUD interface with an overview of items and an edit item/new item form is a breeze to build.

You can only swap out parts of your GUI if you have built those parts as Components. A Page is a monolithic thing in Wicket, and once you switch to another Page, you are very likely to lose state, unless you take precautions.

Less drastic than swapping Components is are several other features in Wicket that can be used on a per-Component basis:

  • toggling the visibility of a Component while staying on the same Page

  • changing the model of a Component to make it show something else, also while staying on the same Page

Component nesting

So you have a developed a nice CRUD interface to edit your Students and Professors, your Employees and Managers, Meeting Room Locations and whatelse. But they are all on different Pages.

And then you think: I want to make it easy to switch between editting a Meeting Room and an Employee, and provide a nice interface based on tabbed panels.

So, you want to nest these Pages inside a TabbedPanel from the Wicket-extensions library. Oops. Not possible, because a Page cannot be nested inside a Component.

Or you have created a Page that shows an Appointment, but now someone wants you to change your code and display the Appointment alongside a panel that shows the meeting room in which the appointment takes place. You could change your ShowAppointmentPage to include a new ShowMeetingRoomPanel (Panels are a kind of Component), but that can't work because you also need to show the appointment alongside the calendar in another use case.

So you change the superclass of ShowAppointmentPage from WebPage to Panel, create a ShowMeetingRoomAppointmentPanel that has two child Components: ShowMeetingRoomPanel and ShowAppointmentPanel.

Would have been much easier if you started with ShowAppointmentPage out as a Component to begin with, right?

Making Components first-class citizens

So what about the drawbacks? Bookmarkable links (links that keep working after the user has finished with the current session) can only be made to Pages, not Components.

If you don't need many bookmarkable links, this is not a problem. You can create two or three subclasses of WebPage for your application and the only thing each of them needs to do is place the one Component in the middle of the Page, and pass the Component anything from the link that the Component needs to work correctly.

But if you need many bookmarkable Pages, this becomes tedious and error-prone.

(You may want to look at your reasons for having bookmarkable Pages; for example, if you need them because you are using Google Analytics, there are other ways of telling Google Analytics what the user is viewing, that are possibly much more useful and fine-grained than can be offered by the URL.)

The solution, in essence, can be quite simple: have one Page that will create any Component that you ask it to.

Just as you can access any Wicket Page if you know its Java class name, you could make a Page that takes as a parameter the name of the class of a Component that you would want to put on that Page.

Just as with Pages, you could check if the constructor of the Component is declared to take an argument of type PageParameters, and forward any PageParameters to that Component.

And just as with Pages, you can restrict access to a Component class using an IAuthorizationStrategy implementation. But because nobody expects Components to be directly accessible (yet), it is a good idea to allow only access to Components that implement a certain marker interface or are annotated with a certain annotation.

I have created such a thing. It is called ComponentPage.

The core of it consists of this method:

    public static void addPageComponent(Page page, String componentId, PageParameters parameters) {
        Class<? extends Component> componentClass = loadComponentClass(parameters);
        checkInstantiationAllowed(componentClass);
        Component component = instantiateComponent(componentClass, componentId, parameters);
        page.add(component);
    }

First it looks up the right class for the Component from the url parameters, then it instantiates that Component using reflection, passing along the PageParameters if the Component wants them, and thirdly it adds the Component to the Page. You can download the entire class, including an example of a Component that uses the replace method to add itself recursively, in this .tar.gz. This is a source tree that can be built with maven.

The central part of ComponentPage is a static method. This means that you can also use the ComponentPage functionality from your own Page. This may be easier when you want to migrate an existing application, or if you want to setup a layout using a Wicket Page.

Erwin Bolwidt

Comments:

Nice post! Kudos.

Posted by showman on January 13, 2011 at 03:43 PM CET #

good

Posted by 65.170.41.5 on April 22, 2011 at 12:33 AM CEST #

Post a Comment:
  • HTML Syntax: Allowed

Calendar

Feeds

Search

Links

Navigation