Howto: Composite Behaviors
Example: Confirmation messages
A straightforward example is a confirmation message that asks a user to confirm a (potentially) destructive action before proceeding. One way of doing this is using an attribute on an HTML element that requires confirmation,
In the above example, the
runOnLoad function is a function defined elsewhere that makes sure that the confirmModifyOnclick function is run when the document is loaded. This function modifies all onClick event handlers for elements that have the confirmationMessage attribute to display a confirmation message before proceeding, allowing the user to cancel.
The standard approach in wicket
The standard approach in wicket is to implement this with behaviors. In this case, we need to add two behaviors to a component that requires confirmation:
Nevertheless, this approach is a bit problematic for the following reasons:
- You need to do two things (add two behaviors) just to accomplish one task (confirmation).
- If in the future the implementation of confirmation popups changes code might be changed in
many places to accomplish this.
It would be better to use one behavior and to write code like this:
In this way, any implementation changes are localized in the confirmation behavior and the code is conceptually close to reality: we must do one thing in the code to accomplish one thing in the application.
Composite behavior, first attempt
A solution is to use the composite design pattern and create a composite behavior.
This can be done as follows:
At construction, the behavior is passed a list of behaviors that it must use. In the bind method of the composite behavior, the composite behavior adds each of its individual behaviors to the component to make sure they are used. Note that the composite behavior also implements IHeaderContributor so that header contributions are also taken into account.
Using this composite behavior, defining a confirmation behavior is just as easy as creating a subclass and passing in the two behaviors that it requires.
Composite behavior, now it works...
Nevertheless, we are not there yet. The composite behavior of the previous example has one flaw and this is that behaviors themselves cannot be internationalized. The problem is that behaviors are constructed in a composite behavior subclass before their parent component is known. As a result, it is impossible to use internationalization. And internationalization is required for the confirmation behavior if we want to internationalize the confirmation messages.
The solution is simple. Instead of creating the behaviors at constructions of the composite behavior we need to delay adding them until their parent component is known, and this is in the bind method. So we modify the composite behavior as follows:
Now the subclass of CompositeBehavior should implement createLocalizedBehaviors to create the behaviors it requires. Using the component which is passed in, localization can be done.