Wicket models inside of ListView
Posted by doubleA on May 16, 2007
I have been using Wicket for about 6 months now and the Model concept still confuses me from time to time. For all of my repeating views, I had been using ListView for it’s simplicity (until I recently found out that DefaultDataTable is better all around). For a ListView, which is still useful depending on the situation, you have to determine how to populate the data for your ListItem within the populateItem method.
For simplicity and ease of use, my first approach had always been something like:
listView = new ListView("domainObj.interestsAsList") {protected void populateItem(final ListItem item) {final Interest interest = (Interest)item.getModelObject();
item.setModel(new CompoundPropertyModel(interest));
item.add(new Label("interestType.name"));item.add(new Label("productType.name"));item.add(new Label("value"));item.add(new Label("startDate"));item.add(new Label("endDate"));}
};
add(listView);
The key line of code is the item.setModel(…). This code sets a compoundProperyModel on the ListItem which allows me to simply using the familar Wicket property matching for the labels that are added to the ListItem. The resulting code is very simple and clean because I can create my labels with just a name and Wicket takes care of calling the proper getter based on the label name.
This worked well, but then I was poking around in AppFuseLight and found another technique that does not use the property matching. The same example as above would be written as:
listView = new ListView("domainObj.interestsAsList") {
protected void populateItem(final ListItem item) {
final Interest interest = (Interest)item.getModelObject();
item.add(new Label("interestType.value", interest.getInterestType().getValue() ));
item.add(new Label("productType.value", interest.getProductType().getValue() ));
item.add(new Label("value", interest.getValue() ));
item.add(new Label("dateEffective", interest.getDateEffective()+""));
item.add(new Label("dateExpiry", interest.getDateExpiry()+""));
}
};
add(listView);
This new code is a bit more verbose in that you have to explicitly set the values for each label. However, there is one big advantage to this approach. The wicket id of your label in the html is independent of the methods for the object being displayed. Also, it’s easier to find errors when the API changes because you are using Java code instead of the property match strings.
I have now converted all of my ListViews to use the new approach that uses explicit setting of the label values because I really like the extra layer of abstraction between the object API and the HTML wicket:id values. This abstraction does create more typing and more code, but I think the tradeoff is worthwhile.
But, for an even better repeater solutions, see the DataTable and DefaultDataTable. These objects have more Java code, but there is only a single
Jonathan Locke said
There is one difference between those two pieces of code, however. In the second case, each Label is being hardwired to a Model with a simple string in it (see Label constructor). That model won’t ever update if the Interest model changes.
The first example won’t either, but that’s because the compound property model is being created directly on the POJO.
If you created that model as folllows AND you converted the interestsAsList property to a List of some IModel implementation (say, InterestModels), your whole list would update any time the model changed:
item.setModel(new CompoundPropertyModel(item.getModel()))
If you did that all the time, you could make a subclass of ListView called CompoundListView.
The trick to really getting models to work for you in Wicket is to make all your session stored data some kind of model and work on classes to make that easy. Instead of manually wrapping an Interest in a model, you can make an InterestModel that just solves that problem and start passing around InterestModels. If you need lists of interest models, you can build a model class for that like InterestModelCollection.
Jonathan Locke said
In a future version of wicket, generics will make it possible to create generic models. Instead of having a StoryModel, you could have a WhateverModel<Story>.
doubleA said
Jonathan, thanks for the pointers, but I’m a little confused about the InterestModel concept. Would the InterestModel not wrap an Interest object? If that is the case, would the InterestModel have getters for any properties that need to be accessed?
My confusion is around the getObject() method in the IModel interface. If I’m not simply wrapping my POJO, what would be returned by the getObject() method.
Jonathan Locke said
InterestModel would be a class you define. possibly a subclass of LoadableDetachableModel or some other class. it would hold on to an id and load the Interest object on demand when someone outside calls IModel.getObject(). IModel.getObject() is going to return your POJO. it’s just going to indirect it so you can hang onto your POJO by db id instead of by reference.