I don’t believe starting with Anonymous Inner Classes (AIC) is a good way to get started when doing event driven programming. For this post, I took the Cheesr application from the Wicket in Action book and removed the AICs and put them in their own classes. The end result is an Index.java and an Index.html with a visible one for one correlation between Wicket tags and the Java code. The second thing I like is that you can see in the structure of the callback classes for the event driven model. Here is the code. First, I show the Index.java, its corresponding Index.html and the three callback classes: AddCheeseCB.java, CheckoutLink.java, and CheesePageList.java. Note that in the callback classes that by providing the @Override annotation that the compiler will warn if somehow you misspell your method that overrides the default behavior that usually does nothing. You can download the whole code for this sample and import it into Eclipse using File->Import…->Existing Project into Workspace. Navigate to the directory where you unzipped zebra04.zip.
Below is the html for Index.html.
<div wicket:id="cheeses">
<h3 wicket:id="name">Gouda</h3>
<p wicket:id="description">Gouda is a Dutch...</p>
<p>
<span wicket:id="price">$1.99</span>
<a wicket:id="add" href="#">add to cart</a>
</p>
</div>
<div wicket:id="navigator"></div>
<div id="cart">
<div wicket:id="shoppingcart"></div>
<input type="button" wicket:id="checkout" value="Check out" />
</div>
This is the corresponding Java code for Index.java. Note how “cheses”, “navigator”, “shoppingcart”, and “checkout” line up nicely between the above html wicket tags and the references in the Index.java.
PageableListView cheeses =
new CheesePageList("cheeses", getCheeses(), 4, this, getCart());
add(cheeses);
add(new PagingNavigator("navigator", cheeses));
add(new ShoppingCartPanel("shoppingcart", getCart()));
add(new CheckoutLink("checkout",getCart()));
Now time for the callback classes. Some of the generic parameters still might be off, but I think this emphasizes what you can do and how the structure works. I put the callbacks in their own package.
First is AddCheeseCB. I think rather than using the <T> parameter, I should have used what the link actually uses, which I believe is <String>, but I am a little unclear on that at them moment. If you know, let me know.
public class AddCheeseCB<T> extends Link<T> {
private Cart myCart;
private static final long serialVersionUID = 1L;
public AddCheeseCB(String id, IModel<T> model, Cart cart) {
super(id, model);
myCart = cart;
}
@Override
public void onClick() {
Cheese selected = (Cheese) getModelObject();
myCart.getCheeses().add(selected);
}
}
Below is the code for the CheckoutLink. Note the behavior that we are implementing is onClick and isVisible. When we construct the object for the behavior, we have to give it a reference to the cart, hence the constructor with the additional arguments. I made the default constructor private. I am not sure if this is the correct approach, but it works for now.
public class CheckoutLink extends Link<String> {
Cart myCart;
private static final long serialVersionUID = 1L;
private CheckoutLink(String id) {
super(id);
}
public CheckoutLink(String id, Cart cart) {
super(id);
myCart = cart;
}
@Override
public void onClick() {
setResponsePage(new CheckOut());
}
@Override
public boolean isVisible() {
return !myCart.getCheeses().isEmpty();
}
}
Now the code for the PageableListView. I believe I specified the generic parameter <Cheese> correctly on this one.
public class CheesePageList extends PageableListView<Cheese> {
private Cart myCart;
private Index myIndex;
private static final long serialVersionUID = 1L;
public CheesePageList(String id,
IModel<? extends List<? extends Cheese>> model, int rowsPerPage) {
super(id, model, rowsPerPage);
}
public CheesePageList(String id,
List<Cheese> list, int rowsPerPage,
Index index, Cart cart) {
super(id, list, rowsPerPage);
myIndex = index;
myCart = cart;
}
@Override
protected void populateItem(ListItem<Cheese> item) {
Cheese cheese = item.getModelObject();
item.add(new Label("name", cheese.getName()));
item.add(new Label("description", cheese.getDescription()));
item.add(new Label("price", "$" + cheese.getPrice()));
Cart myCart = myIndex.getCart();
// Create a real callback rather than an anonymous inner class!
AddCheeseCB myAddCheese = new AddCheeseCB("add", item.getModel(), myCart);
item.add(myAddCheese);
}
}
Download it and try it out. In order to launch it, all you have to do is have Maven installed and execute mvn jetty:run. Wicket is looking like it provides a good clean approach to developing Web Applications, but there is still more to explore, especially with the model that I wrote about in the last post.