Wicket Model Concepts

Introduction

While Models are one of the fundamental concepts in Wicket the details are hard to understand if you are new to the framework. Models are a level of abstraction between a component and the data this component uses. This is necessary as Wicket persists its components and it is not always desirable that the whole dataobject is persisted as well. Even more complex operations such as transformations can be performed. A clever use of Models can significantly reduce the space used by the session storage and speed up the application as well. Always keep in mind that all objects implementing IModel are persisted so care has to be taken which fields are transient and which are not.

The two base classes are:

public interface IDetachable extends IClusterable {
    void detach();
}

public interface IModel<T> extends IDetachable {
    T getObject();
    void setObject(final T object);
}

Concepts

###Loadable Detachable Models###

Loadable detachable models are one way to prevent that too much data is saved in the session. The concept of a loadable detachable model is that the detach() method is invoked after the model has been used which then removes the actual object from the model. So what happens if the model object is needed again? A load() method is called which is responsible for repopulating the model object. A common case is for database entities where the actual entity is detached and only the id of the entity stays in the model, so that the load method can perform a database fetch with the given id to repopulate the model. Wicket already provides a convenience class LoadableDetachableModel.java where only the load() method needs to be implemented.

So what if you do want to repopulate the model from a database but the Model is not implemented as an anonymous inner class which might than use a DAO of a component to refill the model Object? The answer are Injected Models which are discussed in the next section.

###Injected Models###

While components can use the @SpringBean annotation to inject fields with spring beans, models can not. You need to manually inject the beans, but then you can use the @SpringBean annotation as well. This is most likely used in loadable detachable models so it is easy to implement an abstract class that uses this mechanism:

public abstract class InjectedLoadableDetachableModel<T> extends LoadableDetachableModel<T> {

    public InjectedLoadableDetachableModel() {
        super();
        this.inject();
    }

    public InjectedLoadableDetachableModel(T object) {
        super(object);
        this.inject();
    }

    private void inject() {
        Injector.get().inject(this);
    }
}

So when you derive models from this class, you can define a field that is annotated with @SpringBean and can use the bean in the load() method to reattach the model.

###Chained Models###

Chained Models are Models that depend on a parent model and therefore do not contain any data themselves apart from a pointer to the parent object. The only thing they do is to specify how to retrieve data from the parent model(object). Wickets PropertyModel does kind of the same thing, but it uses the field names and is therefore not refactorable.

An abstract base class might look like the following:

public abstract class AbstractChainedModel<P, T> implements IModel<T> {

    protected IModel<? extends P> parentModel;
    
    public AbstractChainedModel(IModel<? extends P> parentModel) {
        this.parentModel = parentModel;
    }

    @Override
    public void detach() {/* Noop, depending on parent */}

    @Override
    public abstract T getObject();

    @Override
    public abstract void setObject(T object);
}

When deriving from this class you must implement the getObject() and setObject() methods to specify how to get/set the data from the parent object.

###List Models###

Wickets ListView uses a ListItemModel that does reference a specific item from a ListViews model object. However you can also use a more general class to achieve the same for example when working with simple repeating views:

public class ListItemModel<T> implements IModel<T> {

    private IModel<? extends List<T>> listModel;

    private int idx;

    public ChainedListItemModel(IModel<? extends List<T>> listModel, int idx) {
        this.listModel = listModel;
        this.idx = idx;
    }

    @Override
    public void detach() {/* NOOP */}

    @Override
    public T getObject() {
        return this.list.getObject().get(this.idx);
    }

    @Override
    public void setObject(T object) {
        this.list.set(this.idx, object);
    }
}

The ListItemModel simply saves a reference to a model containing a list and saves the index of the references item, so it is able to retrieve and set the object at the referenced position.

###Pitfalls###

  • When using ListModels be aware that it can have massive side effects when the ordering of the List changes but the child Models are not regenerated as they will suddenly reference a different item of the list.
  • When using LoadableDetachableModels be aware that the content of the model object might have changed in the meantime. This might lead to a confusing back button behavior as the page will display the current state of the entity and not the one that was used on the page when displayed the first time. However this can also be desired

Meta

Created:

Updated: 2015-12-15 23:12