Home > Open Source, Uncategorized, Web Frameworks, Wicket > The Wicket Component Model Explained

The Wicket Component Model Explained

October 30th, 2007 deevis

Wicket is a Java/HTML approach to building the guts of a J2EE web application, and I must admit that when I first heard of it I was both eagerly excited (to try it) and more than mildly pessimistic (that an application written in Wicket would be maintainable beyond a certain trivial size ). The past few months I’ve heard only good things about Wicket, and haven’t heard anything in the negative. I’m eager to benchmark it and check it out for performance on the Application Servers, but figure first things first – let’s tear it apart, see what makes it tick, and find out what really matters in Wicket.

So, basically everything ( apart from the WebApplication itself ) in Wicket is a Component. Now the Wicket Component is no slouch; it’s a 3,000+ line abstract class with loads of functionality built right in. I’ll dig into this base class later, but for now, let’s focus on the two immediate subclasses of Component: MarkupContainer and WebComponent.

MarkupContainers include things like: WebPage, RedirectPage, Form, FormComponent, FormComponentLabel, Button, CheckBox, TextArea, and TextField.

WebComponents include things like: Image, Include, Label.

I’m surprised, initially, to find controls such as Button, CheckBox, TextArea and TextField as MarkupContainers and not as WebComponents.

Also of interest is that subclassing is used to achieve behavior that might seem best placed in attributes of some of the classes. For example, to create a hidden input field, I might expect to create a TextField instance and set its visible property false. Instead, TextField is subclassed as HiddenField. This is also true for adding password characteristics (PasswordTextField) or making the input required (RequiredTextField). This same pattern of subclassing is used most thoroughly for Links ( AjaxFallbackLink, BookmarkablePageLink, DownloadLink, PageLink, PopupCloseLink, and ResourceLink ).

So, there’s a very rich Component-Model existing within Wicket for creating all the elements you’ll need on a web page, but where does the web page come from? For this we look to another subclass of MarkupContainer. The class WebPage is the primary analog to a conventional webpage. So much so, that the Wicket convention is actually to sit an HTML file of the same (prefix) name as the (simpleName) of the Java class extending WebPage. That is, if you were to write a new class called FunPage like this:

package org.javatech.wicket.examples;
class FunPage extends WebPage {}

Then you would have an HTML file named FunPage.html located in org/javatech/wicket/examples which would contain the HTML used in rendering the webpage.

Cool.

But this is still a bit too abstract – should we really bother writing (yet another) Hello World application for Wicket? No – there are plenty of them out there. Instead, let’s discuss how the Components get added to the WebPage and then how the HTML is able to provide the layout used to create the desired web page. And since every website since the beginning of time has forms on it, let’s show how to add a form.

Adding a form using Wicket Components and a Wicket WebPage:

When I think of an html form, my mind drifts towards thoughts of Maps where the form element’s names are the keys and the values entered by the user are the corresponding values. Then there are just a few other lingering details like the form’s name, whether to use POST or GET, and to what URL the form data should be sent. Having isolated the Component Form as the candidate for the job, I’m immediately stunned by the Constructor which takes an id and/or an IModel. The id makes sense. I mean, really, every html element should have one. But what on god’s green earth is an IModel? I know one thing, it’s an interface – score one for the I-school of thought! Upon inspection, it turns out that an IModel is simply a wrapper for a model object used by a component. It provides methods to get/set the actual model object and to drill down into a nested model object. This being learned, I see that it isn’t required for such humble beginnings as just getting a form to work and shall be forgotten for at least two days now.

So, our first form will be called DemoForm and starts out on the Java side as:

package org.javatech.wicket.pages;

import wicket.markup.html.WebPage;
import wicket.markup.html.basic.Label;
import wicket.markup.html.form.Button;
import wicket.markup.html.form.Form;
import wicket.markup.html.form.TextField;

public class DemoForm extends WebPage {

public DemoForm() {
Form f = new Form( “myForm” );
f.add( new TextField( “myTextField” ));
Button button = new Button(”mySubmitButton”);
f.add( button );
f.setDefaultButton( button );
add( f );
}
}
Which should look something like:

When I start Wicket and hit the URL, I get an exception, because I haven’t created the appropriate HTML file.
WicketMessage: Markup of type ‘html’ for component ‘org.javatech.wicket.pages.LandingPage’ not found

So, it turns out that you need to have EVERY component added in the Java represented in the corresponding HTML. In this case, the HTML would need to contain:

<form wicket:id="myForm">
<input wicket:id="myTextField" />
<input type="submit" wicket:id="mySubmitButton"></button>
</form>

So I gotta admit that my Spidey-sense is tingling at this point. This has the potential to grow quite ugly – having to manage the same essential layout in both Java code and in the HTML. More on that later – so, now my form comes up and works.
I can type into the textbox and click on the button. When I do click on the button, however, I’m taken to a urHell:
http://localhost:8080/hello_wicket/?wicket:interface=:24:myForm::IFormSubmitListener – SAY WHAT?

So, enough suspense. Here are the remaining hoops that Wicket requires you to jump through:
1) You must have a backing model object for your form.
2) You must explicitly implement onSubmit() on the Form object being used.
3) The onSubmit() method will ( in Java code! ) specify the response page for the form.
4) You must (manually) pass the model object to the Confirmation Page.

We’ll have a new class called DemoFormModel defined as:


package org.javatech.wicket.pages;

import java.io.Serializable;

public class DemoFormModel implements Serializable {

private String myTextField;

public String getMyTextField() {
return myTextField;
}

public void setMyTextField(String myTextField) {
this.myTextField = myTextField;
}
}
An instance of DemoFormModel will be instantiated in the constructor of DemoForm and will then be passed ( in the new onSubmit() method ) to the DemoFormConfirmation WebPage. DemoForm now looks like:


package org.javatech.wicket.pages;

import wicket.markup.html.WebPage;
import wicket.markup.html.form.Button;
import wicket.markup.html.form.Form;
import wicket.markup.html.form.TextField;
import wicket.model.CompoundPropertyModel;

public class DemoForm extends WebPage {

public DemoForm() {
Form f = new Form( “myForm” ) {
@Override
protected void onSubmit() {
DemoFormModel o = (DemoFormModel)getModelObject();
DemoFormConfirmation landingPageSubmission = new DemoFormConfirmation( o );
setResponsePage( landingPageSubmission );
}

};
f.setModel( new CompoundPropertyModel( new DemoFormModel() ));
f.add( new TextField( “myTextField” ));
Button button = new Button(”mySubmitButton”);
f.add( button );
f.setDefaultButton( button );
add( f );
}
}
The Java and HTML for DemoFormConfirmation look like:

DemoFormConfirmation.java

package org.javatech.wicket.pages;

import wicket.markup.html.WebPage;
import wicket.markup.html.basic.Label;

public class DemoFormConfirmation extends WebPage {

public DemoFormConfirmation(DemoFormModel o) {
this.add( new Label( “myTextField”, o.getMyTextField() ));
}
}
DemoFormConfirmation.html

You entered:<span wicket:id="myTextField"></span>

So, in a mighty big nutshell, we needed to create 5 files to pull off a simple form submission and confirmation:

DemoForm.java, DemoForm.html, DemoFormModel.java, DemoFormConfirmation.java, and DemoFormConfirmation.html.

DemoForm specifies the form, the components on the form, the model object the components will bind to, and the form’s onSubmit() method. The onSubmit() method is responsible for retrieving the model object, instantiating the DemoFormConfirmation instance with that object, and finally redirecting to that instance.

Then, DemoForm.html needs to reference the components added in DemoForm.java by the same String id’s and DemoFormModel.java needs to have properties which match these names. DemoFormConfirmation.java and DemoFormConfirmation.html have the same sort of coupling but on a smaller scale ( 1 field only ).

The big lesson here is that naming conventions are super important and having a reliable, predictable mechanism for coming up with wicket id’s is going to be essential. Fortunately, there are numerous ways to do this with the simplest being a ModelObject first driven development cycle. Once your ModelObject is created, then it’s fieldNames should be used verbatim in both the Components added on the Java side and the wicket:id’s in the HTML.

I’m not particularly excited about hardcoding site navigation inside of onSubmit() methods in Java code. I’ve been completely brainwashed that this should be configurable behavior, but I’ve seen configurable behavior become a royal pain in the butt and am willing to entertain this backward seeming restriction. Besides, there are way to dynamically wire this stuff together if one is determined to do so ( custom code at this point in time ).

Share and Enjoy:
  • Print this article!
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • StumbleUpon
  • Technorati
  • TwitThis
Comments are closed.