Wicket Component: DropDownChoice
One Wicket component seems to draw a number of questions on the Wicket-User mailing list, the DropDownChoice component. DropDownChoice creates a simple select list, but there are a number of options to this seemingly trivial component. This short article hopes to explain some of the ways to use DropDownChoice, and its sister class, IChoiceRenderer.
Like most Wicket components, DropDownChoice has an overloaded constructor. The first argument is the component id, which must correspond to the wicket:id in the markup:
new DropDownChoice("country"...);
<select wicket:id="country"></select>
Next, we need to tell the DropDownChoice what objects to present as options in the list. The easiest way is to simply pass in a list of objects you want in the select list:
Listcountries = new ArrayList (); countries.add("Argentina"); countries.add("Brazil"); countries.add("Chile"); DropDownChoice country = new DropDownChoice("country", countries);
However, by passing the list directly to the DropDownChoice, the list of countries is stored in the user’s session with the component, taking up valuable memory. The way around this problem is to use a LoadableDetachableModel:
IModel countries = new LoadableDetachableModel() {
public Object load() {
List l = new ArrayList();
l.add("Argentina");
l.add("Brazil");
l.add("Chile");
return l;
}
};
DropDownChoice country = new DropDownChoice("country", countries);
If you’re not familiar with Wicket’s models, the briefest explanation is models provide a data abstraction for Wicket components. Models abstract how components display, and optionally update, data. For the LoadableDetachableModel, the load() method is called at the beginning of the request cycle and the model is detached when the request cycle completes. This means the data loaded in the load() method isn’t serialized to the user’s session.
In a world where Strings are the most complicated datatype, this would be the end of the DropDownChoice discussion. Sadly, that’s not the case. Objects are more complicated and still need to be displayed in a select list. Let’s look at the IChoiceRenderer interface.
IChoiceRenderer allows the developer to determine which values will be used for the display and id values. First, let’s make a Country object:
public class Country {
private String digraph;
private String name;
public Country(String digraph, String name) {
this.digraph = digraph;
this.name = name;
}
// ... getters and setters omitted.
}
Now we’ll update the LoadableDetachableModel to use this new object:
IModel countries = new LoadableDetachableModel() {
public Object load() {
List l = new ArrayList();
l.add(new Country("ar", "Argentina"));
l.add(new Country("br", "Brazil"));
l.add(new Country("cl", "Chile"));
return l;
}
};
DropDownChoice country = new DropDownChoice("country", countries);
Since the Country object is more complicated than a simple String, we need to introduce a specialized renderer:
IChoiceRenderer renderer = new IChoicerRenderer() {
public Object getDisplayValue(Object obj) {
Country c = (Country) obj;
return c.getName();
}
public String getIdValue(Object obj, int index) {
Country c = (Country) obj;
return c.getDigraph();
}
};
countries.setChoiceRenderer(renderer);
And that’s it! (Keep in mind that all of these classes can be broken out into reusable components and shared across several applications.)
What does our HTML look like:
Before render (What the developer sees): <select wicket:id="countries"></select> After render (What the user sees): <select wicket:id="countries"> <option value="ar">Argentina</option> <option value="br">Brazil</option> <option value="cl">Chile</option> </select>
To recap:
- DropDownChoice presents a select-list to the user.
- Models should be used to provide data to components.
- IChoiceRenderer allows developers to customize how DropDownChoice presents options.
The next Wicket component I’m presenting is the RadioGroup.