Quite frequently, I'm using custom Wicket components to render model objects. Objects can sometimes be null, in that case a specific div is displayed. In the component HTML rendering code, I thus have two div's, one for the "null" case, and one for the "non-null" case, with some other inner markup. One is displayed while the other is masked.
<div wicket:id="toDisplayWhenObjectIsNull">
...
</div>
<div wicket:id="toDisplayWhenObjectIsNotNull">
<span wicket:id="label">...</span>
<table wicket:id="table">...</table>
...
</div>
The problem I face is that Wicket force me to entirely build the two div, even if the model object is null. In all calls to sub-components building (labels, tables, etc...) I have to check for nullness, which is cumbersome and error-prone:
X myX = getModel().getModelObject();
Label label = new Label("label",
myX == null ? null : formatY(myX.getY()));
The first solution to this would be to split the non-null part in a specific wicket sub-component, either as it's own class or an inner class of the master component; and inserting this component in place of the "non-null" div. But that double the number of needed files (resources, HTML, java code). This is not ideal.
The second solution, generic, would be to create a "decorator" component to encapsulate any other component, and check for nullness on it's model object. If the component is null, then it would display a standard div, and if not, it would rely on the decorated component. I tried to implement this using borders or composite panels, but I can't manage to make it work. What I would like to achieve is something like this:
// Client code, Java
ViewXPanel xpanel = new ViewXPanel("xpanel", new Model<X>(x));
add(xpanel);
// HTML
<div wicket:id="xpanel"/>
OR, if necessary, make the client responsible of "nullability" of the displayed component, using something like this in the client code:
// Client code, Java
ViewXPanel xpanel = new NullableDecorator(?, ViewXPanel(...));
add(xpanel);
What I did in order to be able to display null values was that I made my own Label subclass, in which I override onComponentTagBody() to replace the empty string with a placeholder:
/**
* Since the converter is not invoked for null values, override this so that
* empty string can be replaced with null display value.
*/
@Override
public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag)
{
String value = getDefaultModelObjectAsString();
replaceComponentTagBody(markupStream, openTag,
value.isEmpty()? "N/A": value);
}
This will make it possible to use
// Client code, Java
add(new MyLabel("label");
// HTML
<div wicket:id="label"/>
You will of course have to customize with your preferences for localization and such, but I hope you get the big picture from the provided example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With