Dear fellow wicket users,
Following is a call for your opinion and a vote on how Wicket should behave
with regards to post-constructing components in 1.5. The final solution that
is chosen will affect the way you construct your components and your component
hierarchies.
First, some background on what onInitialize is and why you should be using it.
THE PROBLEM
Historically, we've generally used our component constructors to build our
component's hierarchy. We call add() to put new components in our custom
component from our component's constructor:
public class MyPanel extends Panel {
private final IModel<String> name = Model.of();
public MyPanel(String id) {
add(new TextField<String>("name", name));
}
}
This can cause problems, however, when initializing a component requires
knowledge of the component's markup, or requires that the component's path in
the hierarchy is known (during construction, the component is not yet added to
any parent component).
THE SOLUTION
onInitialize was introduced to Component. This gives us a place to initialize
our component's hierarchy after our component has been added to its parent. As
a result, our component has a path to its page, making markup, parent and
sibling components accessible.
THE BAD
The downside of initializing your component hierarchy in onInitialize as
opposed to from the constructor, are:
1. You need to move all your component hierarchy initialization code into a
separate method. Usually, this is trivial work (cut/paste), but it is work
that needs to be done nonetheless.
2. You cannot put your components in final instance fields anymore.
3. You should never do anything with components from custom methods. You
should only ever reference your components directly from overridden wicket
methods, such as onConfigure, onBeforeRender, onSubmit, etc.
A little more on #3:
If you add custom methods to your component, eg.
void makeRequired() {
nameField.setRequired(true);
}
You allow things like: new MyPanel().makeRequired();
That would throw a NullPointerException, since nameField is no longer
initialized in the constructor, but much later, before the component first gets
rendered.
I would argue, though, that any custom methods that touch components are to be
avoided at all cost. Component behaviors should never be touched except from
wicket lifecycle methods (you probably want onConfigure) or right after you
construct it: add(new TextField<String>("name", name).setRequired(true)); If
you need a custom method such as makeRequired, it shouldn't touch components,
it should set state that gets used by onConfigure:
void makeRequired() {
this.required = true;
}
void onConfigure() {
setRequired(required);
}
(Obviously, the example is rather silly..)
Fast-forward to https://issues.apache.org/jira/browse/WICKET-3218, it seems
onInitialize has a problem.
THE PROBLEM
With regard to pages, if you add a component to a page from the constructor,
onInitialize is invoked immediately. As a result, onInitialize code is ran
even though your instance hasn't been fully constructed yet. This is bad.
The real issue here is that we can combine component hierarchy initialization
with constructors and onInitialize in a single class. However, doing so causes
dangerous situations. We really should do only one, not both at the same time.
THE SOLUTIONS
This is where we need your vote. Currently, two proposed solutions exist:
1. Make onInitialize final for all Pages. As a result, onInitialize will still
get called when the page is constructed and components are added, but you won't
be able to use it, avoiding the risk of you using it in a manner that requires
your instance to be constructed.
2. Make adding components from the constructor illegal. This would have to be
by means of a runtime exception thrown whenever you try to add components to
the hierarchy from the constructor.
THE GOOD
1. This will be least painful for existing code. In all components, you can
still mix constructor an onInitialize use, in pages, you can't use
onInitialize, but in all likelihood, the largest amount of existing wicket code
out there still uses constructors to create component hierarchies.
2. The safer onInitialize will always and consistently be used for any
component hierarchy construction. This also requires the good code habits that
you shouldn't be doing anything with your components outside of wicket
lifecycle methods.
THE BAD
1. Mixing constructor and onInitialize usage, whether it's within one class or
between different classes in your type and component hierarchy, is and will
always be dodgy. You really shouldn't do it. Also, to future-proof your code,
you should really already be moving your initialization code into onInitialize.
Additionally, this introduces an API inconsistency: For pages, you cannot use
onInitialize, for everything else, you should. That's not very intuitive.
Additionally, it makes refactoring pages into panels and back a real pain
(we've had to do this quite often already).
2. Checking for add() usage with a runtime exception is not very neat. You'll
only notice your mistake at runtime (albeit, very soon. And during your
migration to 1.5, you should expect more trouble than just this). Probably the
biggest down-side is that a lot of people will need to move code around, from
constructors into onInitialize (it can be argued that you really should be
doing this anyway).
REFERENCES
The original discussion on the onInitialize issue with Pages:
http://mail-archives.apache.org/mod_mbox/wicket-dev/201012.mbox/%[email protected]%3E
The JIRA issue:
https://issues.apache.org/jira/browse/WICKET-3218
Please refer to these if you're not entirely sure about the arguments of either
side.
Cast your vote for the future shape of wicket's component hierarchy
initialization API.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]