Apache Wicket
It is a Java web application framework that focuses on making web development more object-oriented and component-based. Unlike traditional web frameworks that rely heavily on HTML templating with logic embedded in tags or scripts, Wicket allows developers to build web pages using pure Java and HTML with a clear separation of concerns.
Key Features of Apache Wicket:
- Component-Based Architecture: You build your UI using reusable Java components like buttons, forms, panels, etc.Each component is tied to an HTML template file.
- Pure Java Programming: All the logic is written in Java. No need for XML configuration or scripting inside HTML.
- Separation of Concerns: HTML templates are clean and contain no Java code, and Java code has no HTML markup — promoting maintainability.
- Type Safety: Thanks to Java’s strong typing, Wicket apps are often more robust and less error-prone.
- Stateful by Default: Wicket keeps component state on the server, which can simplify complex interactions, though it can increase memory usage.
- Built-in AJAX Support: AJAX behaviors can be added easily to components for dynamic content updates without writing JavaScript.
- Internationalization (i18n): Easy support for localization via properties files and resource bundles.
Further information
Link: Apache Wicket
Apache Wicket – Best Practice
QWickie – Eclipse-Plugin
Allows you to jump to the component in the HTML/JavaCode by Ctrl+Clicking on the WicketId.
jRebel
Enables hot deployment in HTML, properties, and Java code. This makes code changes immediately visible without requiring a server restart.
Wicket Models
Models contain the values that should be displayed or edited in the components. They implement the IModel interface, which has only two methods: setObject()
and getObject()
. Using models ensures that Wicket detects changes to the underlying objects and thus knows which parts of the GUI need to be re-evaluated to generate the final HTML. Furthermore, it is also possible to change the values at a later time. This is not possible if the objects are used directly.
The concrete implementation of the various models differs considerably. The model system corresponds to the facade design pattern. Models are generally serialized and stored in the session, which is why the data must be serializable. However, the data can also be detached if this should be prevented or if it is not serializable.
Data should always be passed and stored in components via models.
It should be noted that models are only updated by FormComponent after both the conversion and validation have been successfully completed.
Static Models
With static models, the contained value does not change.
add(new Label("helloMessage", new Model<String>("Hello WicketWorld!")));
or with one of the factory methods:
add(new Label("helloMessage", Model.of("Hello WicketWorld!")));
This is a static model because the value in the model does not change. If the component needs to react dynamically, dynamic models must be used.
Label label = new Label("name", new Model(person.getName()));
is also a static model. In this case, a change to the person's name would not result in a change to the displayed text.
Dynamic Models
Dynamic models repeatedly evaluate the data as soon as it is changed. This means that the component always displays the current value. There are the following options to achieve this:
IModel Interface
Implementation of the IModel Interface (getObject or setObject)
PropertyModel
This model references a subobject/member of a bean via a property path.
This is done via reflections.
Label label = new Label("streetName", new PropertyModel(person, "address.street"));
This model is null-safe. This means that even if the address were null, an empty string would be returned, and no exception would occur. Even the root object can be null.
Access to Arrays, Lists, Maps
arrayElement.0.value (returns a value from the first list element)
arrayElement[0].value (returns a value from the first array element)
map[key].value.. (access to maps)
CompoundPropertyModel
With the CompoundPropertyModel, the wicketId (which identifies the element in HTML) is also used as a property expression. e.g.:
setDefaultModel(new CompoundPropertyModel(person));
Label label = new Label("address.street");
Furthermore, Wicket searches the component hierarchy until it finds a suitable model, since the component itself hasn't defined one. This makes it difficult to read.
Another disadvantage is that the refactoring problem is now exacerbated by the fact that the property names are now also used in the HTML.
DetachableModel
As described above, models (including the data) are persisted in the session.
If the data is large, the session can grow significantly, limiting the page history (which is also contained in the session).
To work around this, the LoadableDetachable model can be used. A load method must be implemented, which is called when the data needs to be reloaded (normally after each request).
The object is deleted after the detach()
call.
Another use case is when you always want to read and display the most recent data, e.g., from a database. With the LoadableDetachableModel, the data is reloaded each time. Alternatively, you can reset the ModelObject in the onConfigure function.
AbstractReadOnlyModel
This model only implements getObject()
(setObject()
returns a NotImplementedException) and should be used for displays if you want to prevent the underlying object from being modified.
Attention: The getObject()
may be called many times within a request – time-consuming operations should not be encapsulated in such a model.
ResourceModels
These models are used when text from a resource file is to be used. They go through the localizer, search for the corresponding text, and fill it with parameters if necessary.
ResourceModel
The ResourceModel takes a key as a parameter and uses it to resolve the text to be displayed.
add(new Label("greetings", new ResourceModel("label.greetings")));
To make the text more dynamic, the StringResourceModel can be used.
StringResourceModel
A model can be specified here that will be used to resolve the parameters.
add(new Label("greetings", new StringResourceModel("label.greetings", this, new Model(user))));
The corresponding resource property file:
label.greetings=Welcome, ${name}!
Wicketeer
Wicketeer provides a way to use the PropertyModel in a type-safe manner. Example
IModel<String> streetModel = model(from(myPersonObject).getAdressData().getHomeAdress().getStreet());
The Wicketeer project should be available in all clients and should be the first choice for model implementation. Caution: If the "myPersonObject" parameter can be null, this will result in NPEs at runtime. To avoid this, the theoretically null object must be wrapped in its own model.
Never pass objects to components
Always wrap objects in models. This makes it possible to subsequently change/replace the object. Objects directly are serialized and cannot be changed afterwards.
Avoid passing components to other components
This creates a dependency between the individual components. If so, this is usually necessary to add one component to the AjaxTarget of another. It's better to use Wicket events.
Always pass the model to the parent component using super(model).
This way, Wicket takes care of the detachment, and the model can be evaluated using the generic getDefaultModel()
method.
Typed Panels and Pages
Typed panels and pages make working with models much easier. This saves you a lot of casting and warnings from the IDE.
Do not use factories for components
Factories prevent further inheritance of components.
- Constructor to prepare the models
- onInitialize to add the components
- onConfigure to control visibility, behavior that can change with each request.
Do not override isVisible and isEnabled
These methods are called very frequently during request processing. It is better to set them in onConfigure.
Use a request cache
If multiple identical services are used in individual components and these are rendered on a page, this can potentially lead to performance issues. This can be optimized by, for example, maintaining a cache of the called services in the request scope. However, you must be careful about data changes that would require an update.
Use Wicket:enclosure with care
If the Wicket:enclosure
tag is used, it can sometimes lead to the Java code and markup becoming out of sync. The reason is that wicket:container is only considered during rendering. If multiple children are contained in the tag, the visibility will only be correct for the child specified in the tag. This can cause problems, for example, during testing. An alternative is to use EnclosureContainer.
Remove Comments
Comments in HTML should be avoided if they are visible to the end user. However, good documentation in HTML is sometimes very important, even in HTML. Apache Wicket offers two solutions for this.
<wicket:remove>
Everything within this tag is removed during rendering.
Application#getMarkupSettings().setStripComments(true)
This removes all HTML comments before rendering.