More on models
Let us take a closer look at models.
The HelloWorld example program demonstrates the simplest model type in Wicket:
The first parameter to the Label component added in the constructor is the Wicket id, which associates the
Label with a tag in the
HelloWorld.html markup file:
The second parameter to Label is the model for the Label, which provides content which replaces any text inside the
<span> tag that the Label is attached to. The model being passed to the Label constructor above is apparently a String. But if we look inside the Label constructor, we see that this constructor is merely there for convenience - it wraps its String argument in a Model object like this:
If we look up the inheritance hierarchy for Label, we see that
WebComponent, which in turn extends
Component. Each of these superclasses has only two constructors. One
Component constructor takes a String id:
And the other takes a
String id and an
IModel is the interface that must be implemented by all Wicket model objects. It looks like this:
And the base interface IDetachable looks like this:
These details are not especially interesting to a beginning model user, so we will come back to this later. What we need to know right now is that Model is an IModel and therefore it can be passed to the Component(String, IModel) constructor we saw a moment ago.
If we take a peek inside Model, we'll see that the Model object is simply a wrapper that holds a Serializable object:
Model's object value is Serializable in order to support deployment of web applications in clustered environments. Note also that since IModel extends IDetachable, which extends Serializable, all IModel wrapper classes are themselves Serializable. So all Wicket models are therefore serializable, which means that they can be replicated between servers in a cluster. Don't worry if you don't fully understand this. We will talk more about clustering later.
Working with POJOs
While using a String like we did in the above example (
'Hello World') is convenient, you will usually want to work with domain objects directly. To illustrate how Wicket supports working with domain object, we use the following Plain Old Java Object (POJO) for our examples:
Like we have seen above, the simplest way of providing e.g. a
Label a model, is just to provide a string, and let the
Label implicitly construct one for you.
We can create the model explicitly like:
This has the same effect as the first example, but as we now have the reference to the model, we can use it e.g. to share it with other components like we did in our little interactive example earlier.
Using models like this works fine as long as there are no changes in the underlying data that should be displayed. You could call these models static or push models, as you have to synchronize any data changes to the model itself.
While static models are the easiest to use, it is often a good idea to let your models get their current objects dynamically from some source so that you can be certain that they display the most recent value. You could call these models dynamic or pull models.
PropertyModel class allows you to create a model that accesses a particular property of its nested model object at runtime. This property is accessed using an expression language which has a syntax similar to OGNL (see http://www.ognl.org) (until version 1.1, Wicket actually did use the OGNL library, however it has been replaced by custom implementation). The simplest
PropertyModel constructor is:
which takes a model object and a property expression. When the property model is asked for its value by the framework, it will use this property expression to access the model object's property.
Looking at our example POJO, the property expression
"name" can be used to access the
"name" property of any Person object via the
getName() getter method. Also, when updating a model, using property expression
"name" has the effect of calling
setName(String). Hence construct a property model to use our POJO, give it a property expression
"name" and set it on a
TextField, not only will that
TextField display the current value of the
"name" property, it will also update the
"name" property with any user input provided.
More complex property expressions are possible as well. For example, you can access sub-properties via reflection using a dotted path notation, which means the property expression
"person.name" is equivalent to calling
getPerson().getName() on the given model object. Arrays can be accessed as in "
persons.name". As you can see, PropertyModels are quite a powerful way to give Wicket components access to model objects.
More information on the syntax
More information on the allowed property expressions can be found either in the
wicket.util.lang.PropertyResolver JavaDoc or on the Property Expression Language wiki page
Compound property models
If you have looked at some examples of Wicket already, you may have wondered why components, which generally must have a model, can be constructed with only an id, omitting any model. There are three reasons for this.
The first, most obvious reason is that not all components actually need a model. Panels and pages, for example, do not use models, though you might find it handy to store a model in these components.
The second reason to construct a
Component with no model is that it is sometimes impossible to have a model ready for the call to
super(String, IModel) in the
Component's constructor. In this case, the
Component can be constructed without a model, and the model can be later set with a call to
The third reason to construct a component with no model is that some components are actually bound to their models at runtime. When an attempt is made to retrieve the model for a Component which does not yet have one, instead of failing and returning null, the
getModel() method in
Component calls the method
initModel(), which gives the
Component an opportunity to lazy-initialize, returning a model on-the-fly.
This mechanism opens up all kinds of possibilities for model initialization. One particularly convenient and efficient lazy model initialization strategy is employed by the
BoundCompoundPropertyModel classes to allow containers to share their models with children. By sharing model objects like this, fewer objects are created, which saves memory, but more importantly, it makes replication of models much cheaper in a clustered environment.
To use a
CompoundPropertyModel, you simply set one as the model for any container. However, a very typical place to install a
CompoundPropertyModel is on a
Page. This is natural, because the compound model itself is usually the model that is being displayed by the
Page or edited by the
To see a
CompoundPropertyModel in action, take a look at the FormInput example . In the
InputForm nested class in
Form is constructed like this:
If we look at the next few lines of the Form's constructor, we see statements like:
these statements are adding FormComponents to the
Form. But they are not explicitly attached to any model. Instead, they will inherit the
CompoundPropertyModel for the form at runtime. When the framework needs the model for a
RequiredTextField, it will call
This method retrieves the text field's model by calling
getModel(), which is implemented like this:
RequiredTextField has no model, the
initModel() method will be called, resulting in a search up the containment hierarchy for a
CompoundPropertyModel that can be inherited by the model-less child:
The text field will discover the
CompoundPropertyModel for the Form and set that as its own model. So
getModel() on the text field ultimately returns the model of the
Next, the nested model object is retrieved from the model by passing in the
Component (in this case the
RequiredTextField) as a parameter to
IModel.getObject(Component). This is the magic that allows the
CompoundPropertyModel to retrieve the right value from the Form's model for the
Component that is asking for it:
The retrieval is done by using the name of the
Component as the property expression to access the model. In the case of this component
getStringProperty() method would be called on the
The model's object value is available in this way to the component for processing. In the case of the
stringProperty text field component, it will convert the value to a String. In the case of other
RequiredTextField's in the FormInput form, a conversion may occur based on the third parameter to the Component. For example, this component:
will convert the contents of the text field to and from an Integer value.
BoundCompoundPropertyModel has been removed in Wicket 6!
In the event that you need more flexibility or property expressions power in your compound models, you can use the
BoundCompoundPropertyModel. This class provides three methods you can call on the container's model object to bind a given child Component to a specific property expression and/or type conversion:
To see how this might be used, suppose that the
stringProperty value needed to be bound to the property expression
"person.stringProperty". In the
FormInput constructor, we'd say something like this:
Form is constructed without a model. We then create the Form's model as a
BoundCompoundPropertyModel, set it as the Form's model and finally use it to bind the
RequiredTextField to the desired property expression.
String Resource Models
Localization of resources in Wicket is supported by the
Localizer class. This provides a very convenient way to retrieve and format application and component specific resources according to the
Locale that the user's Session is running under. In many cases, it will be sufficient to simply retrieve a localized String in this way, but if a formatted, localized string is desired as a model, a
StringResourceModel may be used.
StringResourceModel-s have a resource key (for looking up the string resource in a property file), a
Component, an optional model and an optional array of parameters that can be passed to the Java
MessageFormat facility. The constructors look like this:
A very simple example of a
StringResourceModel might be a
Label constructed like this:
where the resource bundle for the page contains an entry like this:
By adding a model, we can use the property expressions to interpolate properties from the model into the localized string in the resource bundle. For example, suppose we wanted to add the name from a
User object to the greeting above. We'd say:
and have a resource like:
User has a
getName() method exposing its
Component class has convenience method which you may find useful:
If we do not want to use the property expressions/introspection, but instead want to to pull a fresh value from the model on each request in a more strongly typed manner, we could provide our own implementation of
Note that in the above example, we extended from
Model instead of implementing
IModel directly, and we did not bother writing
setObject() (as for the label it is not needed).
By implementing your own models, you can support any kind of model behaviour you can think of, though it is usually a more verbose way of doing things.