Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: formatting

...

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:

Code Block
<html>
    <head>
	<title>Wicket Examples - helloworld</title>
	<link rel="stylesheet" type="text/css" href="style.css"/>
    </head>
    <body>
	<span wicket:id="mainNavigation">main nav will be here</span>
	<span wicket:id="message">Message goes here</span>
    </body>
</html>

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 Label extends WebComponent, which in turn extends Component. Each of these superclasses has only two constructors. One Component constructor takes a String id:

Code Block
public Component(final String id)
{
  setId(id);
}

And the other takes a String id and an IModel:

Code Block
public Component(final String id, final IModel model)
{
  setId(id);
  setModel(model);
}

IModel is the interface that must be implemented by all Wicket model
objects. It looks like this:

Code Block
public interface IModel extends IDetachable
{
  public Object getNestedModel();
  public Object getObject(final Component component);
  public void setObject(final Component component, final Object object);
}

...

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 extends AbstractModel, which implements IModel. So a Model is an IModel and therefore it can be passed to the Component(String, IModel) constructor we saw a moment ago.

...

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:

Code Block
import java.util.Date;

public class Person
{
  private String name;
  private Date birthday;

  public Person()
  {
  }
  public String getName()
  {
    return name;
  }
  public void setName(String name)
  {
    this.name = name;
  }
  public Date getBirthday()
  {
    return birthday;
  }
  public void setBirthday(Date birthday)
  {
    this.birthday = birthday;
  }
}

...

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.

...

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.

Property models

The 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:

Code Block
public PropertyModel(final Object modelObject, final String expression)

...

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 TextField display the current value of the "name" property, it will also update the "name" property with any user input provided.

...

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 setModel().

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 CompoundPropertyModel and 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 Form or Page. This is natural, because the compound model itself is usually the model that is being displayed by the Page or edited by the Form.

To see a CompoundPropertyModel in action, take a look at the FormInput example . In the InputForm nested class in FormInput.java, the Form is constructed like this:

...

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 Form.

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 getStringProperty() method would be called on the Form's model.

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:

...

Wiki Markup
To see how this might be used, suppose that the {{stringProperty}} value needed to be bound to the property expression {{"person\[0\].stringProperty"}}.  In the {{FormInput}} constructor, we'd say something like this:

...

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.

StringResourceModels 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:

Code Block
add(new Label("greetings", new StringResourceModel("label.greetings", this, null)));

...

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:

...

Code Block
label.greetings=Welcome, ${user.name}!

where User has a getName() method exposing its "name" property.

Custom models

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 IModel:

...

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).

...