Overview
Apache Wicket provides an interesting out of the box component called a ‘Wizard’ that does just what you’d expect it to, create an easy to use Wizard workflow that you can insert into your page. It provides next, previous and cancel buttons as well as tracking the state between steps and enabling the ‘Finish’ button when appropriate. You can see the genericwizard in action here : http://www.wicket-library.com/wicket-examples/wizard/
The base markup that comes with the wizard is ‘adequate’ in that ‘it works’ but its complex table based structure is at odds with modern CSS based markup making it difficult to adapt at times. Luckily (ok, by design..) its totally and completely customizable (as all good Wicket Things are) however doing so is a bit obtuse and generally left as an exercise for the reader. Here is a step by step for how you customize the look/feel of your wizard in Wicket. I won’t be explaining the base case, the link above details that. This tutorial won’t go “all the way”, if you really want to customize to the ‘nth degree its easy enough once you understand how the basics work.
When in doubt, download the wicket source and follow the bouncing ball, thats all I did. This quick tutorial also touches on how to customize a few other pieces of the Wizard process but the core of it is demonstrated in the first section.
You can download my Quickstart here: wizard-quickstart.zip
Create A Wizard without tables!
Lets create a Table-less Wizard! A Wizard is basically a Panel that extends ‘Wizard’, lets call ours “YourCustomWizard” and build out its base methods, assign it a model and add a few steps. In the Real World your steps are probably going to be dynamic. If you want a quick tutorial on that let me know but its pretty easy to get through. Note that the HTML on the right has NO TABLES. You are now in control of your own CSS destiny.
Put simply (thanks Nathan!) “To override the default markup, make a markup file using the same basename as your custom Wizard class (e.g., “YourCustomWizard.html”) with the following wicket:id attributes: form, overview, header, view, feedback and buttons. With that knowledge you can stop reading. Continue on for code samples.
Your welcome. NOTE: The Old Fat Wizard is the html that ships with the Wicket source.
(/src/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/wizard/Wizard.html)
|
package wicket.quickstart;
import wicket.Component; import wicket.extensions.wizard.Wizard; import wicket.extensions.wizard.WizardModel;
public class YourCustomWizard extends Wizard { /* * Create a wizard with a default model * and three custom steps. Each step * is a form with some default data * in the model for example purpose. */ public YourCustomWizard(String id){ super(id); WizardModel model = new WizardModel(); YourObject theObj = new YourObject(); theObj.setElementdescription(“First item”); theObj.setElementlabel(“Label me”); theObj.setElementvalue(“my value”);
YourObject theObj2 = new YourObject(); theObj2.setElementdescription(“Second item”); theObj2.setElementlabel(“Label me2″); theObj2.setElementvalue(“my value2″);
YourObject theObj3 = new YourObject(); theObj3.setElementdescription(“Third item”); theObj3.setElementlabel(“Label me3″); theObj3.setElementvalue(“my value3″);
model.add(new YourWizardStep(theObj)); model.add(new YourWizardStep(theObj2)); model.add(new YourWizardStep(theObj3)); init(model); } /** * On finish. * * Put any specific logic you want when the * user hits the “finish” button eventually * Here we print to error log and send them * to the home page. * Normally used for saving unclean objects * to database if you did not do it in the WizardStep */ @Override public void onFinish() { System.err.println(“DONE WITH THE WIZ!”); setResponsePage(Index.class); } /** * On cancel. * * Put your cancel logic in for when users * hit the ‘Cancel’ button. * I send them to the home page */ @Override public void onCancel() { setResponsePage(Index.class); }
protected Component newButtonBar(java.lang.String id){ return new ButtonBarPanel(id, this); } protected Component newOverviewBar(java.lang.String id){ return new OverviewPanel(id); }
} |
The New Lean And Mean Wizard (YourCustomWizard.html)
<wicket:panel> <div> <form wicket:id=“form”class=“wicketExtensionsWizardForm”> <span wicket:id=“overview”></span> <span wicket:id=“header”></span> <div wicket:id=“view”></div> <span wicket:id=“feedback”></span> <div class=“buttons”> <span wicket:id=“buttons”></span> </div> </form> </div> </wicket:panel>
The Old Fat Wizard (Wizard.html)
<wicket:panel> <div class=“wicketExtensionsWizard”> <form wicket:id=“form” class=“wicketExtensionsWizardForm”> <table class=“wicketExtensionsWizardOuterTable”> <tr> <td valign=“top”> <span wicket:id=“overview”></span> </td> <td valign=“top”> <table class=“wicketExtensionsWizardInnerTable”> <tr class=“wicketExtensionsWizardHeaderRow”> <td valign=“top” class=“wicketExtensionsWizardHeader”> <span wicket:id=“header”></span></td> </tr> <tr class=“wicketExtensionsWizardViewRow”> <td valign=“top” class=“wicketExtensionsWizardView”> <div wicket:id=“view” class=“wicketExtensionsWizardViewInner”></div></td> </tr> <tr class=“wicketExtensionsWizardFeedbackRow”> <td valign=“top” class=“wicketExtensionsWizardFeedback”> <span wicket:id=“feedback”></span></td> </tr> <tr class=“wicketExtensionsWizardButtonBarRow”> <td valign=“top” class=“wicketExtensionsWizardButtonBar”> <span wicket:id=“buttons”></span></td> </tr> </table> </td> </tr> </table> </form> </div> </wicket:panel>
|
Now that you have the base system of Custom Wizardry in place lets look at some more components you can customize.
What’s in a Wizard Step?
Lets first look at our example step “YourWizardStep()”. In our case this is a panel that extends the WizardStep class with a nice form that collects ‘elementvalue’ and displays two fields, ‘elementlabel’ and ‘elementdescription’. Its important to stop for a second and understand that I am using the same class for *all* of my wizard steps. You are not bound by this, you can create a multitude of wizard steps and add them to your wizard as you see fit. Your Wizard can do things like branch steps based on the previous set, force users to stay on a step and you can even embed a full wizard within a step for some Wizard On Wizard action.
|
package wicket.quickstart;
import wicket.extensions.wizard.WizardStep; import wicket.markup.html.basic.Label; import wicket.markup.html.form.TextField; import wicket.model.CompoundPropertyModel; import wicket.model.PropertyModel;
public class YourWizardStep extends WizardStep { public YourWizardStep(YourObject object1){ setModel(new CompoundPropertyModel(object1)); add(new Label(“elementlabel”)); add(new Label(“elementdescription”).setEscapeModelStrings(false)); TextField theField = new TextField(“elementvalue”,new PropertyModel(object1, “elementvalue”)); theField.setRequired(true); add(theField); } /* * This is called when the next button i clicked, * I assume you want to use your for for data gathering.. * Normally you’d save the object the the database * Or just leave it in memory and save it in the Wizard onFinish() */ public void applyState(){ System.err.println(“Applying state!”); setComplete(true); // Set this step as done, you should put custom logic here } } |
<html xmlns:wicket> <wicket:panel> <fieldset> <legend> <span wicket:id=“elementlabel”></span> </legend> <span wicket:id=“elementdescription”>[Element Description]</span> <br/> <input type=text wicket:id=“elementvalue”> </fieldset> </wicket:panel> </html> |
Add an Overview
The overview is just a bit of markup that is placed in the element named ‘overview’. Nominally this is used to give the user an ‘overview’ of what this wizard is all about etc. This is pretty vanilla but we are nothing if not complete.
|
package wicket.quickstart;
import wicket.markup.html.panel.Panel;
/* * The Overview panel is displayed in the Overview div automatically. * It serves no special purposes other then for the default wizard * to display common information across pages. */ public class OverviewPanel extends Panel { public OverviewPanel(String id){ super(id);
} }
|
<html xmlns:wicket> <wicket:panel> Overview Panel </wicket:panel> </html>
|
Pressing My Buttons
The other thing developers usually want to customize are the buttons. You may need to add custom classes in order to Prettify them, you might want to use images. Buttons always need some help. The Wizard by default places buttons in a wicket element component with the id ‘buttons’. To create your own ButtonBar you just need to … wait for it .. extend Panel and make sure you have some specific markup in the page to indicate to wicket what element to attach its actions to.
|
package wicket.quickstart;
import wicket.extensions.wizard.CancelButton; import wicket.extensions.wizard.FinishButton; import wicket.extensions.wizard.NextButton; import wicket.extensions.wizard.PreviousButton; import wicket.extensions.wizard.Wizard; import wicket.markup.html.panel.Panel;
public class ButtonBarPanel extends Panel { public ButtonBarPanel(String id, Wizard wizard){ super(id); add(new PreviousButton(“previous”, wizard)); add(new NextButton(“next”, wizard)); add(new YourLastButton(“last”, wizard)); add(new CancelButton(“cancel”, wizard)); add(new FinishButton(“finish”, wizard)); } }
|
<wicket:panel>
<div> <input wicket:id=“previous” type=“submit” value=“previous” /> <input wicket:id=“next” type=“submit” value=“next” /> <input wicket:id=“last” type=“submit” value=“last” /> <input wicket:id=“cancel” type=“submit” value=“cancel” /> <input wicket:id=“finish” type=“submit” value=“finish” /> </div>
</wicket:panel> |
Easy! However if you look closely you will notice that One Of These Buttons is doing his own thing. We are attaching a button called ‘YourLastButton’ to the wicket element ‘last’. This is a custom button implementation as an example of what can be done. Note that you are not controlling the view of a button, just the controller. You’d do this if you wanted to add some customer behavior to a button or override one of the methods like isEnabled(). The most general case is to add an update workflow to the onClick() that updates the model somehow.
|
jfdpackage wicket.quickstart;
import wicket.extensions.wizard.IWizard;
import wicket.extensions.wizard.IWizardModel;
import wicket.extensions.wizard.WizardButton;
public class YourLastButton extends WizardButton
{
private static final long serialVersionUID = 1L;
public YourLastButton(String id, IWizard wizard)
{
super(id, wizard, “org.apache.wicket.extensions.wizard.last”);
}
public final boolean isEnabled()
{
return getWizardModel().isLastAvailable();
}
public final boolean isVisible()
{
return getWizardModel().isLastVisible();
}
public final void onClick()
{
IWizardModel wizardModel = getWizardModel();
wizardModel.getActiveStep().applyState();
wizardModel.lastStep();
}
}
package wicket.quickstart;
import wicket.extensions.wizard.IWizard; import wicket.extensions.wizard.IWizardModel; import wicket.extensions.wizard.WizardButton;
public class YourLastButton extends WizardButton { private static final long serialVersionUID = 1L;
public YourLastButton(String id, IWizard wizard) { super(id, wizard, “org.apache.wicket.extensions.wizard.last”); }
public final boolean isEnabled() { return getWizardModel().isLastAvailable(); }
public final boolean isVisible() { return getWizardModel().isLastVisible(); }
public final void onClick() { IWizardModel wizardModel = getWizardModel(); wizardModel.getActiveStep().applyState(); wizardModel.lastStep(); } } |
Summary
This is a basic introduction on how to implement your own Wizard markup in place of the mark-up that the wizard puts out. I had to get through this on a project because throwing the default table based layout into my pages was causing some serious issues. By creating my own extended Wizard classes I was able to totally customize the look/feel of the wizard and ensure pixel perfect design. In my case I needed to use NiceForms which I highly recommend. Particularly since you can now Customize Your Wicket Wizard..
You can do more with Wizards, much more. This tutorial is not meant to be comprehensive. Check out the Wizard API for a total rundown on what can be sub-classed. Take a look at the DynamicWizardStep and other classes, they really open the power of the Wizard. Also refer to the source for the base implementations. Open Source is an invaluable resource so Read The Source. The Wizard code is short and easy to understand since it uses the common Wicket constructs (panels etc). Don’t be intimidated.
Now, Go Forth And Wizardify.


Discussion
No comments for “Customizing Wicket Wizard Markup”