55 Minutes

Welcome to the 55 Minutes blog.
55 Minutes is a web development consultancy in San Francisco building apps for design-led businesses and startups. On this blog we discuss the technology tools of our trade: Ruby on Rails, Django, Sass, OS X, and more.

How to Implement Radio Buttons in Wicket

Implementing radio buttons in a Wicket form is one of the more challenging tasks for developers new to the framework. But setting aside Wicket, even getting the HTML itself right is nontrivial. Let’s take a look at how this is done, starting with the HTML.

HTML

A first cut at the HTML might look like this:

<input type="radio" name="ticker" value="AAPL"/>Apple<br/>
<input type="radio" name="ticker" value="GOOG"/>Google<br/>
<input type="radio" name="ticker" value="MSFT"/>Microsoft<br/>
01-first_cut.html – View Gist

That renders fine, but there is a subtle usability issue: clicking on Apple, Google or Microsoft doesn’t toggle the appropriate radio button. A well-designed form should do this. Furthermore, br tags are a sign that we are inappropriately mixing styling instructions into our markup. We can do better.

<label><input type="radio" name="ticker" value="AAPL"/>Apple</label>
<label><input type="radio" name="ticker" value="GOOG"/>Google</label>
<label><input type="radio" name="ticker" value="MSFT"/>Microsoft</label>
02-improved.html – View Gist

What we’ve done here is used appropriate semantic markup to give the browser more information. The browser now sees that Apple, Google, and Microsoft are actually labels for the radio buttons AAPL, GOOG, and MSFT, respectively. When you click the label, the browser automatically toggles the appropriate radio button. Much better.

Finally, we sprinkle on some CSS to ensure each radio button is rendered on a separate line.

label { display: block; }
03-improved.css – View Gist

Wicket: Static Choices

In a web application you’ll probably deal with two different sets of radio buttons: static radio buttons have the choices hardcoded in the HTML, like we’ve done in the example above; dynamic radio buttons have choices that come from the database. Let’s look at the simpler static case first.

Wicket handles radio buttons using a pair of classes: Radio and RadioGroup. Each radio button is represented by a Radio component; in turn, those Radio components must all be contained in a single RadioGroup. All of these components need to have a corresponding wicket:id in the HTML.

By default, the RadioGroup tag is not rendered. It just serves as a container to hold a group of Radio components. We’ll use Wicket’s wicket:container tag for this purpose, rather than insert a meaningless div or span into our markup.

<form wicket:id="form">
  <wicket:container wicket:id="group">
    <label><input wicket:id="aapl" type="radio" name="ticker"/>Apple</label>
    <label><input wicket:id="goog" type="radio" name="ticker"/>Google</label>
    <label><input wicket:id="msft" type="radio" name="ticker"/>Microsoft</label>
  </wicket:container>
</form>
04-StaticPage.html – View Gist

Notice that we’ve removed the value attributes from the input tags. Wicket will manage them for us.

Our corresponding Java code looks like this:

// Radio buttons must be part of a Form component.
Form form = new Form("form");
add(form);

// This model holds the value corresponding to the radio that the user selected.
// We use a simple Model here, but in practice it would be a PropertyModel to your
// persistent data object.
IModel<String> selected = new Model<String>();

RadioGroup group = new RadioGroup("group", selected);
form.add(group);

// For each radio button, we create it with a hardcoded string value that
// represents that choice. That string value will be passed to our selected
// model (see above) when the form is submitted.
group.add(new Radio("aapl", new Model<String>("AAPL")));
group.add(new Radio("goog", new Model<String>("GOOG")));
group.add(new Radio("msft", new Model<String>("MSFT")));
05-StaticPage.java – View Gist

What’s important to note here is that each Radio component has a model. In Wicket, you don’t have direct access to the POST data being sent to the browser; you only have access to the Radio model. When the form is submitted, Wicket does the following:

  1. Based on the POST data, Wicket determines which Radio component was selected by the user.
  2. Wicket reads the model value associated with the selected Radio component.
  3. Wicket passes this value to the model of the RadioGroup component.

In short: you use models on the Radio components to tell Wicket about the possible values, and the model on the RadioGroup is the user’s chosen value. You’ll see that these basic rules apply to the dynamic case as well.

Wicket: Dynamic Choices

Most of the time we don’t know the values of the radio buttons ahead of time; we don’t necessarily even know how many there will be. Furthermore, each radio button will have a unique identifier and label. How do we generate the HTML and wire up Wicket with this dynamic data?

First we need a way to represent each choice that comes from the database. Let’s assume we have a simple value object for this purpose.

public class Company implements Serializable
{
    private String symbol;
    private String name;

    // getters and setters omitted for brevity

    /**
     * It is very important to provide a meaningful {@code equals()}
     * implementation. Wicket uses this to compare the saved value of the
     * form against the various radio button choices in order to determine
     * which choice should be drawn as selected.
     */
    public boolean equals(Object other)
    {
        if(!(other instanceof Company)) return false;
        return symbol != null && symbol.equals(((Company) other).symbol);
    }
}
06-Company.java – View Gist

Our basic approach will be as follows:

  1. Ask the database for a list of Company objects.
  2. Loop over those objects and create a Radio for each one.
  3. We’ll use Company.symbol to uniquely identify each choice, and Company.name as the display value.

Here’s our HTML:

<form wicket:id="form">
  <wicket:container wicket:id="group">
    <label wicket:id="choice">
      <input wicket:id="radio" type="radio" name="ticker"/>
      <wicket:container wicket:id="label"/>
    </label>
  </wicket:container>
</form>
07-DynamicPage.html – View Gist

The choice section of the markup will be repeated for each choice. For each choice we’ll provide a radio button and a label.

// Radio buttons must be part of a Form component.
Form form = new Form("form");
add(form);

IModel<Company> selected = new Model<Company>();
RadioGroup group = new RadioGroup("group", selected);
form.add(group);

// Exercise left for the reader: load the list of companies from the database.
IModel<List<Company>> companies;

// Construct a radio button and label for each company.
group.add(new ListView<Company>("choice", companies) {
    protected void populateItem(ListItem<Company> it)
    {
        it.add(new Radio("radio", it.getModel()));
        it.add(new Label("label", it.getModelObject().getName()))
    }
});
08-DynamicPage.java – View Gist

This does almost everything we want. If you run this code and view the resulting HTML in the web browser, you’ll notice that Wicket is generating meaningless incremental identifiers for each of the radio buttons, like this:

<input type="radio" name="form:group" value="radio13"/>
09-output.html – View Gist

Ideally we want Wicket to use our own unique identifier, Company.symbol, as the value. You can do this by overriding Radio.getValue(), but this is a bit tedious.

Instead, consider using the RadioChoicesListView component in our very own fiftyfive-wicket library. This component captures all the best practices described in this article, handles overriding Radio.getValue() and helpfully eliminates some of the boilerplate code. The more concise solution is thus:

IModel<Company> selected = new Model<Company>();
// Exercise left for the reader: load the list of companies from the database.
IModel<List<Company>> companies;

// Tell RadioChoicesListView what properties to use for label and ID
ChoiceRenderer<Company> renderer = new ChoiceRenderer<Company>("name", "symbol");

// Wire it all up!
add(new Form("form")
    .add(new RadioGroup("group", selected)
        .add(new RadioChoicesListView("choice", companies, renderer))));
10-DynamicPageImproved.java – View Gist
comments powered by Disqus