11. Wicket models and forms

In Wicket the concept of “model” is probably the most important topic of the entire framework and it is strictly related to the usage of its components. In addition, models are also an important element for internationalization, as we will see in paragraph 12.6. However, despite their fundamental role, in Wicket models are not difficult to understand but the best way to learn how they work is to use them with forms. That’s why we haven’t talked about models so far, and why this chapter discusses these two topics together.

11.1. What is a model?

Model is essentially a facade interface which allows components to access and modify their data without knowing any detail about how they are managed or persisted. Every component has at most one related model, while a model can be shared among different components. In Wicket a model is any implementation of the interface org.apache.wicket.model.IModel:

uml imodel

The main goal of IModel interface is to decouple components from concrete details about the persistence strategy adopted for their data. In order to achieve this level of abstraction IModel defines the two methods required to get and set a data object: getObject() and setObject(). The level of indirection introduced by models allows access data object only when it is really needed (for example during the rendering phase) and not earlier when it may not be ready to be used. In addition to getObject() and setObject(), IModel defines a richer set of methods, mostly meant to work with Java 8 lambdas. We will introduce them in the next paragraph.

Any component can get/set its model as well as its data object using the 4 public shortcut methods listed in the class diagram above. The two methods onModelChanged() and onModelChanging() are triggered by Wicket each time a model is modified: the first one is called after the model has been changed, the second one just before the change occurs. In the examples seen so far we have worked with Label component using its constructor which takes as input two string parameters, the component id and the text to display:

  1. add(new Label("helloMessage", "Hello WicketWorld!"));

This constructor internally builds a model which wraps the second string parameter. That’s why we didn’t mention label model in the previous examples. Here is the code of this constructor:

  1. public Label(final String id, String label) {
  2. this(id, new Model<String>(label));
  3. }

Class org.apache.wicket.model.Model is a basic implementation of IModel. It can wrap any object that implements the interface java.io.Serializable. The reason of this constraint over data object is that this model is stored in the web session, and we know from chapter 6 that data are stored into session using serialization.

In general, Wicket models support a detaching capability that allows us to work also with non-serializable objects as data model. We will see the detaching mechanism later in this chapter.

Just like any other Wicket components, Label provides a constructor that takes as input the component id and the model to use with the component. Using this constructor the previous example becomes:

  1. add(new Label("helloMessage", new Model<String>("Hello WicketWorld!")));

The Model class comes with a bunch of factory methods that makes it easier to build new model instances. For example the of(T object) method creates a new instance of Model which wraps any Object instance inside it. So instead of writing

  1. new Model<String>("Hello WicketWorld!")

we can write

  1. Model.of("Hello WicketWorld!")

If the data object is a List, a Map or a Set we can use similar methods called ofList, ofMap and ofSet. From now on we will use these factory methods in our examples.

It’s quite clear that if our Label must display a static text it doesn’t make much sense to build a model by hand like we did in the last code example. However is not unusual to have a Label that must display a dynamic value, like the input provided by a user or a value read from a database. Wicket models are designed to solve these kinds of problems.

By default the class Component escapes HTML sensitive characters (like ‘<’, ‘>’ or ‘&’) from the textual representation of its model object. The term ‘escape’ means that these characters will be replaced with their corresponding HTML entity (for example ‘<’ becomes <). This is done for security reasons as a malicious user could attempt to inject markup or JavaScript into our pages. If we want to display the raw content stored inside a model, we can tell the Component class not to escape characters by calling the setEscapeModelStrings(false) method.

11.2. IModel and Lambda

With Wicket 8 IModel has been extended with new methods to fully leverage lambdas. The most interesting thing of the new version of IModel is that it provides a default implementation for all of its methods (included setObject()), with the only exception of getObject(). In this way IModel is eligible as functional interface and this greatly simplify the creation of custom models. As long as we need to display a static text it doesn’t make much sense building a custom model, but if we need to display a dynamic value (like the input provided by a user or a value read from a database), defining a model with a lambda expression comes quite in handy.

Let’s say we need a label to display the current time stamp each time a page is rendered. This could be a possible solution:

  1. add(new Label("timeStamp", () -> java.time.LocalDate.now()));

As mentioned above, method setObject() comes with a default implementation. The code is the following:

  1. default void setObject(final T object)
  2. {
  3. throw new UnsupportedOperationException(
  4. "Override this method to support setObject(Object)");
  5. }

This means that models obtained using IModel as lambda expressions are read-only. When we work with forms we need to use a model that support also data storing. In the next paragraph we will see a couple of models shipped with Wicket that allow us to easily use JavaBeans as backing objects.

11.2.1. Lambda Goodies

Most of the default methods we find in IModel are meant to leverage Lambda expressions to transform model object. The following is a short reference for such methods:

  • filter(predicate): Returns a IModel checking whether the predicate holds for the contained object, if it is not null. If the predicate doesn’t evaluate to true, the contained object will be null. Example:
  1. //the filtered model will have a null model object if person's name
  2. //is not "Jane"
  3. IModel<Person> janeModel = Model.of(person)
  4. .filter((p) -> p.getName().equals("Jane"));
  • map(mapperFunction): Returns an IModel applying the given mapper to the contained object, if it is not null. Example:
  1. //the new read-only model will contain the person's first name
  2. IModel<String> personNameModel = Model.of(person).map(Person::getName);
  • flatMap(mapperFunction): Returns an IModel applying the given IModel-bearing mapper to the contained object, if it is not null. Example:
  1. //returns a read/write model for person's first name
  2. //NOTE: LambdaModel will be discussed later.
  3. IModel<String> personNameModel = Model.of(person).flatMap(targetPerson ->
  4. LambdaModel.of(
  5. () -> targetPerson::getName, targetPerson::setName
  6. ));
  • combineWith(otherModel, combiner): Returns an IModel applying the given combining function to the current model object and to the one from the other model, if they are not null. Example:
  1. IModel<String> hello = Model.of("hello");
  2. IModel<String> world = Model.of("world");
  3. IModel<String> combinedModel = hello.combineWith(
  4. world, (thisObj, otherObj) -> thisObj + " " + otherObj);
  5. assertEquals("hello world", combinedModel.getObject());
  • orElseGet(supplier): Returns a read-only IModel using either the contained object or invoking the given supplier to get a default value. Example:
  1. IModel<String> nullObj = new Model();
  2. assertEquals("hello!", nullObj.orElseGet(() -> "hello!");

11.3. Models and JavaBeans

One of the main goals of Wicket is to use JavaBeans and POJO as data model, overcoming the impedance mismatch between web technologies and OO paradigm. In order to make this task as easy as possible, Wicket offers two special model classes: org.apache.wicket.model.PropertyModel and org.apache.wicket.model.CompoundPropertyModel. We will see how to use them in the next two examples, using the following JavaBean as the data object:

  1. public class Person implements Serializable {
  2. private String name;
  3. private String surname;
  4. private String address;
  5. private String email;
  6. private String passportCode;
  7. private Person spouse;
  8. private List<Person> children;
  9. public Person(String name, String surname) {
  10. this.name = name;
  11. this.surname = surname;
  12. }
  13. public String getFullName(){
  14. return name + " " + surname;
  15. }
  16. /*
  17. * Getters and setters for private fields
  18. */
  19. }

11.3.1. PropertyModel

Let’s say we want to display the name field of a Person instance with a label. We could, of course, use the Model class like we did in the previous example, obtaining something like this:

  1. Person person = new Person();
  2. //load person's data...
  3. Label label = new Label("name", new Model(person.getName()));

However this solution has a huge drawback: the text displayed by the label will be static and if we change the value of the field, the label won’t update its content. Instead, to always display the current value of a class field, we should use the org.apache.wicket.model.PropertyModel model class:

  1. Person person = new Person();
  2. //load person's data...
  3. Label label = new Label("name", new PropertyModel(person, "name"));

PropertyModel has just one constructor with two parameters: the model object (person in our example) and the name of the property we want to read/write ( name in our example). This last parameter is called property expression. Internally, methods getObject/setObject use property expression to get/set property’s value. To resolve class properties PropertyModel uses class org.apache.wicket.util.lang.Property Resolver which can access any kind of property, private fields included.

Just like the Java language, property expressions support dotted notation to select sub properties. So if we want to display the name of the Person’s spouse we can write:

  1. Label label = new Label("spouseName", new PropertyModel(person, "spouse.name"));
PropertyModel is null-safe, which means we don’t have to worry if property expression includes a null value in its path. If such a value is encountered, an empty string will be returned.

If property is an array or a List, we can specify an index after its name. For example, to display the name of the first child of a Person we can write the following property expression:

  1. Label label = new Label("firstChildName", new PropertyModel(person, "children.0.name"));

Indexes and map keys can be also specified using squared brackets:

  1. children[0].name ...
  2. mapField[key].subfield ...

11.3.2. LambdaModel

PropertyModel uses textual expressions to resolve object properties. That’s nice but it comes with some drawbacks. For example the expression can not be checked at compile time and is not refactoring-friendly. To overcome these problems with Wicket 8 a new kind of lambda-based model has been introduced: org.apache.wicket.model.LambdaModel. This model uses lambda expressions to get/set model object. Here is the signature of its constructor:

  1. public LambdaModel(SerializableSupplier<T> getter, SerializableConsumer<T> setter)

In the following code we use method references to operate on a specific object property:

  1. Person person = new Person();
  2. IModel<String> personNameModel = new LambdaModel<>(person::getName, person::setName);

As we have seen for Model also LambdaModel comes with factory method LambdaModel.of:

  1. Person person = new Person();
  2. IModel<String> personNameModel = LambdaModel.of(person::getName, person::setName);

11.3.3. CompoundPropertyModel and model inheritance

Class org.apache.wicket.model.CompoundPropertyModel is a particular kind of model which is usually used in conjunction with another Wicket feature called model inheritance. With this feature, when a component needs to use a model but none has been assigned to it, it will search through the whole container hierarchy for a parent with an inheritable model. Inheritable models are those which implement interface org.apache.wicket.model.IComponentInheritedModel and CompoundPropertyModel is one of them. Once a CompoundPropertyModel has been inherited by a component, it will behave just like a PropertyModel using the id of the component as property expression. As a consequence, to make the most of CompoundPropertyModel we must assign it to one of the containers of a given component, rather than directly to the component itself.

For example if we use CompoundPropertyModel with the previous example (display spouse’s name), the code would become like this:

  1. //set CompoundPropertyModel as model for the container of the label
  2. setDefaultModel(new CompoundPropertyModel(person));
  3. Label label = new Label("spouse.name");
  4. add(label);

Note that now the id of the label is equal to the property expression previously used with PropertyModel. Now as a further example let’s say we want to extend the code above to display all of the main informations of a person (name, surname, address and email). All we have to do is to add one label for every additional information using the relative property expression as component id:

  1. //Create a person named 'John Smith'
  2. Person person = new Person("John", "Smith");
  3. setDefaultModel(new CompoundPropertyModel(person));
  4. add(new Label("name"));
  5. add(new Label("surname"));
  6. add(new Label("address"));
  7. add(new Label("email"));
  8. add(new Label("spouse.name"));

CompoundPropertyModel can save us a lot of boring coding if we choose the id of components according to properties name. However it’s also possible to use this type of model even if the id of a component does not correspond to a valid property expression. The method bind(String property) allows to create a property model from a given CompoundPropertyModel using the provided parameter as property expression. For example if we want to display the spouse’s name in a label having “xyz” as id, we can write the following code:

  1. //Create a person named 'John Smith'
  2. Person person = new Person("John", "Smith");
  3. CompoundPropertyModel compoundModel;
  4. setDefaultModel(compoundModel = new CompoundPropertyModel(person));
  5. add(new Label("xyz", compoundModel.bind("spouse.name")));

CompoundPropertyModel is particularly useful when used in combination with Wicket forms, as we will see in the next paragraph.

Model is referred to as static model because the result of its method getObject is fixed and it is not dynamically evaluated each time the method is called. In contrast, models like PropertyModel and CompoundProperty Model are called dynamic models.

11.4. Wicket forms

Web applications use HTML forms to collect user input and send it to the server. Wicket provides org.apache.wicket.markup.html.form.Form class to handle web forms. This component must be bound to

tag. The following snippet shows how to create a very basic Wicket form in a page:

Html:

  1. <form wicket:id="form">
  2. <input type="submit" value="submit"/>
  3. </form>

Java code:

  1. Form<Void> form = new Form<Void>("form"){
  2. @Override
  3. protected void onSubmit() {
  4. System.out.println("Form submitted.");
  5. }
  6. };
  7. add(form);

Method onSubmit is called whenever a form has been submitted and it can be overridden to perform custom actions. Please note that a Wicket form can be submitted using a standard HTML submit button which is not mapped to any component (i.e. it does not have a wicket:id attribute). In the next chapter we will continue to explore Wicket forms and we will see how to submit forms using special components which implement interface org.apache.wicket.markup.html.form.IFormSubmitter.

11.4.1. Form and models

A form should contain some input fields (like text fields, check boxes, radio buttons, drop-down lists, text areas, etc.) to interact with users. Wicket provides an abstraction for all these kinds of elements with component org.apache.wicket.markup.html.form.FormComponent:

uml form component

The purpose of FormComponent is to store the corresponding user input into its model when the form is submitted. The form is responsible for mapping input values to the corresponding components, avoiding us the burden of manually synchronizing models with input fields and vice versa.

11.4.2. Login form

As first example of interaction between the form and its models, we will build a classic login form which asks for username and password (project LoginForm).

The topic of security will be discussed later in chapter 22. The following form is for example purposes only and is not suited for a real application. If you need to use a login form you should consider to use component org.apache.wicket.authroles.authentication.panel.SignInPanel shipped with Wicket.

This form needs two text fields, one of which must be a password field. We should also use a label to display the result of login process. For the sake of simplicity, the login logic is all inside onSubmit and is quite trivial.

The following is a possible implementation of our form:

  1. public class LoginForm extends Form {
  2. private TextField usernameField;
  3. private PasswordTextField passwordField;
  4. private Label loginStatus;
  5. public LoginForm(String id) {
  6. super(id);
  7. usernameField = new TextField("username", Model.of(""));
  8. passwordField = new PasswordTextField("password", Model.of(""));
  9. loginStatus = new Label("loginStatus", Model.of(""));
  10. add(usernameField);
  11. add(passwordField);
  12. add(loginStatus);
  13. }
  14. public final void onSubmit() {
  15. String username = (String)usernameField.getDefaultModelObject();
  16. String password = (String)passwordField.getDefaultModelObject();
  17. if(username.equals("test") && password.equals("test"))
  18. loginStatus.setDefaultModelObject("Congratulations!");
  19. else
  20. loginStatus.setDefaultModelObject("Wrong username or password!");
  21. }
  22. }

Inside form’s constructor we build the three components used in the form and we assign them a model containing an empty string:

  1. usernameField = new TextField("username", Model.of(""));
  2. passwordField = new PasswordTextField("password", Model.of(""));
  3. loginStatus = new Label("loginStatus", Model.of(""));

If we don’t provide a model to a form component, we will get the following exception on form submission:

  1. java.lang.IllegalStateException: Attempt to set model object on null model of component:

Component TextField corresponds to the standard text field, without any particular behavior or restriction on the allowed values. We must bind this component to the tag with the attribute type set to “text” PasswordTextField is a subtype of TextFiled and it must be used with an tag with the attribute type set to “password”. For security reasons component PasswordTextField cleans its value at each request, so it will be always empty after the form has been rendered. By default PasswordTextField fields are required, meaning that if we left them empty, the form won’t be submitted (i.e. onSubmit won’t be called). Class FormComponent provides method setRequired(boolean required) to change this behavior. Inside onSubmit, to get/set model objects we have used shortcut methods setDefaultModelObject and getDefaultModelObject. Both methods are defined in class Component (see class diagram from illustration 9.1).

The following are the possible markup and code for the login page:

Html:

  1. <html>
  2. <head>
  3. <title>Login page</title>
  4. </head>
  5. <body>
  6. <form id="loginForm" method="get" wicket:id="loginForm">
  7. <fieldset>
  8. <legend style="color: #F90">Login</legend>
  9. <p wicket:id="loginStatus"></p>
  10. <span>Username: </span><input wicket:id="username" type="text" id="username" /><br/>
  11. <span>Password: </span><input wicket:id="password" type="password" id="password" />
  12. <p>
  13. <input type="submit" name="Login" value="Login"/>
  14. </p>
  15. </fieldset>
  16. </form>
  17. </body>
  18. </html>

Java code:

  1. public class HomePage extends WebPage {
  2. public HomePage(final PageParameters parameters) {
  3. super(parameters);
  4. add(new LoginForm("loginForm"));
  5. }
  6. }

The example shows how Wicket form components can be used to store user input inside their model. However we can dramatically improve the form code using CompoundPropertyModel and its ability to access the properties of its model object. The revisited code is the following (the LoginFormRevisited project):

  1. public class LoginForm extends Form{
  2. private String username;
  3. private String password;
  4. private String loginStatus;
  5. public LoginForm(String id) {
  6. super(id);
  7. setDefaultModel(new CompoundPropertyModel(this));
  8. add(new TextField("username"));
  9. add(new PasswordTextField("password"));
  10. add(new Label("loginStatus"));
  11. }
  12. public final void onSubmit() {
  13. if(username.equals("test") && password.equals("test"))
  14. loginStatus = "Congratulations!";
  15. else
  16. loginStatus = "Wrong username or password !";
  17. }
  18. }

In this version the form itself is used as model object for its CompoundPropertyModel. This allows children components to have direct access to form fields and use them as backing objects, without explicitly creating a model for themselves.

Keep in mind that when CompoundPropertyModel is inherited, it does not consider the ids of traversed containers for the final property expression, but it will always use the id of the visited child. To understand this potential pitfall, let’s consider the following initialization code of a page:
  1. //Create a person named 'John Smith'
  2. Person person = new Person("John", "Smith");
  3. //Create a person named 'Jill Smith'
  4. Person spouse = new Person("Jill", "Smith");
  5. //Set Jill as John's spouse
  6. person.setSpouse(spouse);
  7. setDefaultModel(new CompoundPropertyModel(person));
  8. WebMarkupContainer spouseContainer = new WebMarkupContainer("spouse");
  9. Label name;
  10. spouseContainer.add(name = new Label("name"));
  11. add(spouseContainer);

The value displayed by label “name” will be “John” and not the spouse’s name “Jill” as you may expect. In this example the label doesn’t own a model, so it must search up its container hierarchy for an inheritable model. However, its container (WebMarkup Container with id ‘spouse’) doesn’t own a model, hence the request for a model is forwarded to the parent container, which in this case is the page. In the end the label inherits CompoundPropertyModel from page but only its own id is used for the property expression. The containers in between are never taken into account for the final property expression.

11.5. Component DropDownChoice

Class org.apache.wicket.markup.html.form.DropDownChoice is the form component needed to display a list of possible options as a drop-down list where users can select one of the proposed options. This component must be used with