Steve Forsyth currently resides in sunny California with his wife and kids. Steve has worked in several different industries as well as worked with several software development technologies. His work has taken him from building financial reports for executives at Franklin Capitol, creating international websites for the worlds largest technology provider to the creation of custom web portals for large scale media applications. Steve is most comfortable with finding the needs of a client and turning those needs into a useful, timesaving tool. He currently has an affection for the Wicket Java application framework and hopes to some day see the end of cross-browser hell. Steve has posted 1 posts at DZone. View Full User Profile

Wicket Tutorial Series: The UI

03.13.2009
| 33869 views |
  • submit to reddit

So… you should now have a fairly good understanding of how to put Wicket together with Spring and Hibernate, creating your DAOs and services and putting that code through the test gauntlet. We can see that our foundation is rock solid… but we’re missing the eye-candy… so let’s hop over to the UI and show you where Wicket really shines.
 

Base Class

Most if not all web applications use some sort of base template to remove duplication such as the header and footer. Wicket has a built-in way of handling this instead of having to use a separate library such as SiteMesh. Wicket uses inheritance to facilitate templates. They provide their own base class called WebPage that our application specific base class will extend from to get started. The WebPage class sets us up with a blank web page in seconds. For our application, we have a simple header/footer that we want all of our pages to use and a very simple menu that I threw into the base page that I named BasePage.


public class BasePage extends WebPage {...

This along with an html page gives us a basic template that all of our pages will extend from.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Mystic Paste</title>
<link rel="stylesheet" type="text/css" href="css/style.css"/>
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="css/ie.css" />
<![endif]-->
</head>
<body>
<div id="leftSide"> </div>
<div id="rightSide"> </div>
<div id="center">

<!-- header -->
<div id="header">
<div id="logo"><a class="logo" href="http://projects.mysticcoders.com/paste"> </a></div>
</div>
<div id="nav">
<ul id="menus">
<li class="cat-item"><a class="home" title="Home" href="http://www.mysticcoders.com/">Home</a></li>
<li wicket:id="newLinkContainer" class="cat-item"><a wicket:id="newLink" href="#" title="New Paste">New</a></li>
<li wicket:id="historyLinkContainer" class="cat-item"><a wicket:id="historyLink" href="#" title="View Paste History">History</a></li>
<li><a class="lastmenu" href="javascript: return false;"> </a></li>
</ul>
</div>
<div id="header_bottom"> </div>

<!-- content -->
<div id="content">
<wicket:child/>
</div>
<div class="clear"> </div>

<!-- footer -->
<div id="footer_left"> </div>
<div id="footer_right"> </div>
<div id="footer_center">
<div id="copyright">Copyright © 2000-2009 Mystic Coders, LLC</div>
</div>
</div>
<div id="logo_footer"><img src="images/logo_bottom.png" width="74" height="57"/></div>
</body>
</html>

This html file sits on the file system in the same package as your BasePage class and is named the same but with a .html extension… BasePage.html. We have decided to separate the java files from the html by putting the html within the same package structure underneath the resources folder. Note the wicket:id attributes and the <wicket:child/> tag… the wicket:id attributes are used within the java code to identify the components and the wicket:child tag is used as a placeholder signaling that any page that extends this page will be filling in the body of the tag. The 2 links with wicket:id attributes are used for menu item links and the surrounding li tags contain wicket:id attributes to facilitate the highlighting of the current page.
 

PASTE IT!

Wicket starts to get fun when we get into forms. We need to create a form that will let the user choose the language type for formatting the pasted content, whether or not this is a private post (not easily guessed url and won’t show in history), and the content itself. We are going to want to make sure that the end result has a fairly simple url that is easy to copy and paste.
 
The first thing we usually do is come up with the page class and the html… so we are going to create a class that extends our BasePage: 


public class PasteItemPage extends BasePage {...

The matching html page again, resides in the same package as the Java class and is named the same.
PasteItemPage.html:

<wicket:extend>
<form wicket:id="pasteForm">
<div id="paste_options">
<ul>
<li>private:</li>
<li><input wicket:id="private" type="checkbox" /></li>
</ul>
<ul>
<li>language:</li>
<li>
<select wicket:id="type" class="language">
<option>Choose One</option>
<option>Java</option>
<option>Groovy</option>
<option>PHP</option>
</select>
</li>
</ul>
</div>
<div id="paste_content">
<div id="textLeft"> </div>
<div id="textRight"> </div>
<div id="textCenter"><textarea wicket:id="content" wrap="off"></textarea></div>
</div>
<div id="paste_submit"><input type="submit" value="Paste" /></div>
</form>
</wicket:extend>

Note the wicket:extend tags which tells Wicket that everything within those tags are the contents that we are interested in… for instance, you could have the whole html file with html/head/body tags if you wanted to and wicket would ignore everything except for the data between the wicket:extend tags.
 
The wicket:id attributes are placed in the form and the form components. These attributes will allow us to create a Wicket form and bind to the form components.
 
Wicket provides components for just about everything you want to do, so we extend the Wicket Form class and add that to our page, we then add our form fields (DropDownChoice, CheckBox and TextArea) to the form.
 
Components in Wicket are hierarchical, you MUST nest/add your components in your java code to match exactly the nesting of your html components. For example, the following snippet is taken from BasePage.html:


<li wicket:id="newLinkContainer" class="cat-item"><a wicket:id="newLink" href="#" title="New Paste">New</a></li>


The corresponding Java code looks like this:

WebMarkupContainer newLinkContainer = new WebMarkupContainer("newLinkContainer");
...
newLinkContainer.add(new BookmarkablePageLink("newLink", PasteItemPage.class));
add(newLinkContainer);

In the html markup, the href tag marked with wicket:id=”newLink” is nested inside of the li tag marked with wicket:id=”newLinkContainer”. We therefore need to match this hierarchy within our corresponding java code. In the Java code, I have created a WebMarkupContainer component with id=”newLinkContainer” to match to our li tag, I then add the nested BookmarkablePageLink with id=”newLink” to the newLinkContainer component. I then add the newLinkContainer component to the page as the newLinkContainer is not contained within any other wicket tags.
 
This nesting can get very deep depending on the web page layout. It is not difficult to keep track of the nesting but sometimes you may forget to fix the html or the Java code when making changes to the either file. However, the Wicket developers built in a clean error message that comes up when you run the application and there is a mismatch between your html and the Java code. For example, if I use our example above and add the newLink to the page rather than to the newLinkContainer, I get the following error message:

WicketMessage: Unable to find component with id 'newLink' in [MarkupContainer [Component id = newLinkContainer]]. This means that you declared wicket:id=newLink in your markup, but that you either did not add the component to your page at all, or that the hierarchy does not match.
[markup = file:/...paste/web/pages/paste/PasteItemPage.html
 

These error messages make it easy to find the problems with the hierarchy rather than guessing as to where the problem might be.
 
The following is the full Java source for our PasteItemPage:

public class PasteItemPage extends BasePage {

@SpringBean
PasteService pasteService;

public PasteItemPage() {
super(PasteItemPage.class);
add(new PasteForm("pasteForm", new CompoundPropertyModel(new PasteItem())));
}

public class PasteForm extends Form {
public PasteForm(String id, IModel model) {
super(id, model);

add(new CheckBox("private"));
add(new DropDownChoice("type", Arrays.asList(LanguageType.JAVA, LanguageType.CSS, LanguageType.HTML)));
add(new TextArea("content"));
}

@Override
protected void onSubmit() {
PasteItem pasteItem = (PasteItem) PasteForm.this.getModelObject();
pasteService.createItem("web", pasteItem);
PageParameters params = new PageParameters();
if (pasteItem.isPrivate()) {
params.put("0", pasteItem.getPrivateToken());
setResponsePage(ViewPrivatePage.class, params);
} else {
params.put("0", Long.toString(pasteItem.getId()));
setResponsePage(ViewPublicPage.class, params);
}
}
}
}

As you might have noticed… Wicket uses Models to back the components. In our case… we use a CompoundPropertyModel which makes it extremely easy to bind to components. It basically tells any component that uses this model to bind the property from the model object with a component using the components id. For instance, we have add(new CheckBox(”private”)); which says that we want to add a CheckBox component with the id of “private” and bind it to the property of our model object with the same name (the private field of PasteItem). I have added the CompoundPropertyModel to the form component which automagically backs all components added to the form but can easily be overridden by just passing in a new model to any components that need a different model. There are many other types of Models to choose from as you may not need or want the CompountPropertyModel due to mismatches in the names and such. The DropDownChoice and TextArea components are bound to the html SELECT and TEXTAREA tags in the same manor.
 
The last piece of the form submission is completed by overriding the onSubmit method of the form and saving our model object with a simple call to our service. That is it for capturing user input and saving it… not sure that it gets much easier than that!
 
As part of the save routine, one other noteworthy tidbit here is how we forward on to the next page:

PageParameters params = new PageParameters();
if (pasteItem.isPrivate()) {
params.put("0", pasteItem.getPrivateToken());
setResponsePage(ViewPrivatePage.class, params);
} else {
params.put("0", Long.toString(pasteItem.getId()));
setResponsePage(ViewPublicPage.class, params);
}

The setResponsePage method is exactly that… we give it the page that we want to forward to… in this case, if it is a private message, then we forward to our page we created for private pastes, otherwise, we forward to our regular public view page. Notice that we create a PageParameters object, Wicket abstracts away the dreadful request object from you and gives you a convenient object for adding and retrieving page parameters. Now… as I mentioned earlier, we want simple urls… so normally, you would put something like params.put(”id”, pasteItem.getId()); and this would pass the request param of id=5 or with Wickets bookmarkable pages, you would see something like http://your.domain.com/view/id/5. We decided we didn’t want the id to show as it provides no use within the url itself… so… Wicket gives us the ability to create our own URL encoding strategy and provides a few already implemented strategies. Within the Wicket Application class that was generated on Day 1, we can add the following:

mount(new IndexedParamUrlCodingStrategy("/view", ViewPublicPage.class));

This tells Wicket that anyone forwarding to my ViewPublicPage will use the IndexedParamUrlCodingStrategy… which works as follows: we add/pull params from the PageParameters using keys of 0, 1, 2… etc. As you can see in our code example, we use 0 as we only have one param. The end result of this is that our url will look something like this:

http://your.domain.com/view/5

This doesn’t seem like much, but it does have a slightly cleaner url and depending on your application can help greatly with SEO.


Published at DZone with permission of its author, Steve Forsyth.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Ojitha Kumanayaka replied on Fri, 2009/03/13 - 4:03am

Great tutorial

Dick Larsson replied on Sun, 2009/03/15 - 7:17pm

Great material, thanks for sharing your knowledge..

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.