Why PropertyModel’s are used
Before Wicket 8, and before Java 8 introduced lambda expressions, you were pretty much forced to use the PropertyModel. Through the magic of reflection, PropertyModel is a very compact and simple way to specify your models for fields without having to write verbose code.
Let’s look at a simple example for a TextField that captures a user’s name, and a ListMultipleChoice for capturing their favorite movie genres. You might do it this way using PropertyModel:
new TextField<String>("displayName", new PropertyModel<String>(user, "displayName"));
new ListMultipleChoice<String>("favGenres", new PropertyModel<List<String>>(user, "favGenres"),
new ListModel<>(List.of("Action","Comedy","Romance","Sci-fi")));
The problem with PropertyModel
The above code is very simple and compact, but it’s difficult to maintain, error prone, and can’t take advantage of modern IDE refactoring tools because it relies on strings.
Imagine you later change the displayName field of User to something else. The IDE won’t be able to automatically change the expression “displayName” passed to the PropertyModel, so unless you remember to change the expressions everywhere, you will have introduced a bug that can only be discovered at runtime.
Modern IDE’s also provide great tools for searching for references, which is very helpful when dealing with large code bases. If you were to search for all references of User.getDisplayName(), the IDE would not be able to see that PropertyModel reference.
You can solve pretty much all of these problems by replacing each instance of PropertyModel with custom implementations of Model, but that would lead to very verbose code. There is a better way!
Using LambdaModel
Using Wicket 8 LambdaModel‘s, we can keep all the benefits IDE’s offer us and maintain code compactness. First, we need to define a few helper functions that we will put into an interface that WebPage‘s will implement:
default <T extends Serializable> IModel<T> objModel(SerializableSupplier<T> getter, SerializableConsumer<T> setter) {
return LambdaModel.of(getter, setter);
}
default <T extends Serializable> IModel<List<T>> objListModel(SerializableSupplier<List<T>> getter, SerializableConsumer<List<T>> setter) {
return LambdaModel.of(getter, setter);
}
We use these helper functions to make our code even more compact and to not directly rely on LambdaModel in case we later want to switch to some other model or an even better implementation introduced in Wicket 10 馃檪
But essentially, they are a shortcut for instantiating a LambdaModel, given a getter and setter method. The first method operates on single values, while the second method operates on List‘s.
We can now update our Wicket fields to use the new models. As you can see the code is almost the same and just as compact:
new TextField<String>("displayName", objModel(user::getDisplayName, user::setDisplayName));
new ListMultipleChoice<String>("favGenres", objListModel(user::getFavGenres, user::setFavGenres), new ListModel<>(List.of("Action","Comedy","Romance","Sci-fi")));
I think PropertyModel’s were a necessary evil in previous versions of Wicket, but now that better alternatives exist, you should definitely replace them.
In general, it’s a great idea to create helper methods like the above, to isolate the usage of specific Model implementations. This makes it much simpler to make changes in large code bases as new versions of Java/Wicket continuously get released.
Check out the full working sample here!