Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

NetBeans 6: Matisse Updates

NetBeans 6 delivers great updates to the Matisse GUI builder. Spend a few minutes with Roman Strobl and get an expert briefing on what's new and what has changed.

Introduction to Groovy Part 3

In this, the third and final installation of Andres' Introduction to Groovy series, you learn about how Groovy handles variable numbers of arguments, named parameters, currying, and more about Groovy operators. Including, some new operators.

Easier Custom Components with Swing Fuse

Swing Fuse (actually just Fuse), is a framework designed to make it easier to create your own custom desktop components. In this article, Daniel Spiewak shows you how to get started and provides sample source code you can download.

Benchmark Analysis: Guice vs Spring

Willam Louth shows how he uses JXInsight Probes to investigate probable performance issues with code bases that he is not familiar with. He also highlights possible pitfalls in creating a benchmark, as well as in the analysis of results.
Replies: 0 - Pages: 1  
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Wicket: Reusable Panels

At 11:43 PM on Jan 4, 2006, R.J. Lorimer wrote:

Last time in my article A Little Bit About Wicket , I extolled the virtues of the MVC framework Wicket in creating modular, reusable components for your Java web application. Today I want to show you a brief run-through of one of the key components for Wicket's reusability - the Wicket wicket.markup.html.panel.Panel .

Wicket Panels are another type of component, like Label or ListView from the previous tip. Unlike Label or ListView, however, a Panel is allowed to have its own associated markup. So while the markup for the ListView 'people' from our previous tip was embedded in the containing page's markup, a Panel has its own markup file, and that makes it much more reusable.

Here is an example. In my previous tip, I had a table that presented a list of Person objects to the user. This page was succinct and simple. Let's say that a new requirement showed up, however, and we now need to show that table on two different pages. We'd like to be able to just put the code for the ListView on both pages, but we now also have to copy the markup, and that's certainly not very 'reusable'.

Instead, we can create a panel. Here is how. First, we must create a subclass of wicket.markup.html.panel.Panel that contains our component tree for the stuff we want to reuse - you'll recognize the ListView from yesterday's tip:

package com.javalobby.tnt.wicket.aloha;
 
import java.util.*;
 
import wicket.markup.html.basic.Label;
import wicket.markup.html.list.*;
import wicket.markup.html.panel.Panel;
 
/**
 * Shows a list of people.
 * 
 * @author R.J. Lorimer
 */
public class PersonPanel extends Panel {
 
	/**
	 * Id constructor.
	 * @param id the Id.
	 */
	public PersonPanel(String id) {
		super(id);
		add(new ListView("people", getPeople()) {
			// This method is called for each 'entry' in the list.
			@Override protected void populateItem(ListItem item) {
				Person person = (Person)item.getModelObject();
				item.add(new Label("firstName", person.firstName));
				item.add(new Label("lastName", person.lastName));
				item.add(new Label("email", person.email));
			}		
		});
	}
	private List<Person> getPeople() {
		List<Person> people = new ArrayList<Person>();
		people.add(new Person("R.J.", "Lorimer", "rj@javalobby.org"));
		people.add(new Person("Rick", "Ross", "rick@javalobby.org"));
		people.add(new Person("Matt", "Schmidt", "matt@javalobby.org"));
		return people;
	}	
	class Person {
		String firstName;
		String lastName;
		String email;
		
		Person(String fName, String lName, String emailAddr) {
			this.firstName = fName;
			this.lastName = lName;
			this.email = emailAddr;
		}
	}	
}

As you can see, it looks very similar to our old Page, but unlike the Page , the Panel has to implement a non-empty constructor, passing the ID up to the parent. That is because all components aside from a Page require an ID, as they are not the root of the component hierarchy.

Next, we need to create a new markup file to associate with this panel titled to match ( PersonPanel.html ):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
<html 
	xmlns="http://www.w3.org/1999/xhtml" 
	xmlns:wicket="http://wicket.sourceforge.net/" 
	xml:lang="en" 
	lang="en">  
<body>
	<wicket:panel>
		<h3>This is the BEGINNING of the Person Panel</h3>
		<table>
			<thead>
				<tr>
					<th>First Name</th>
					<th>Last Name</th>
					<th>Email Address</th>
				</tr>
			</thead>
			<tbody>
				<tr wicket:id="people">
					<td wicket:id="firstName">[First Name]</td>
					<td wicket:id="lastName">[Last Name]</td>
					<td wicket:id="email">[Email]</td>
				</tr>
			</tbody>
		</table>
		<h3>This is the END of the Person Panel</h3>
	</wicket:panel>
</body>
</html>

Looking at the above example, you'll see that we have one new and unique tag - the <wicket:panel> tag. This new tag tells wicket how much of the HTML source actually belongs to the panel. Why is this important? It allows us to provide gobs of template HTML around the panel, or what is often called 'preview markup'. This markup allows for multiple things to be done - first, the panel HTML page can now have custom documentation embedded in it. This would allow us to show it to someone who wanted to use our panel (let's say for the moment that we shipped it as a 'wicket extensions' library), and they could learn how to use it just by looking at this file. In addition, this HTML allows us to supply boilerplate template layout that may not be necessary when the panel is used in context to a page - this allows the preview to look decent (or at least indicative to what you would want to see), without having to do anything magical. You'll also notice that I added a little header so we'd know that the panel was responsible for the rendering.

The next step is to adjust our Page class to use the new, reusable Panel we created, rather than using the ListView that we had before. We can give the panel any ID we want at this point, just like the label or list view we used:

package com.javalobby.tnt.wicket.aloha;
 
import java.util.Date;
 
import wicket.markup.html.WebPage;
import wicket.markup.html.basic.Label;
 
public class AlohaPage extends WebPage {
	
	public AlohaPage() {
		// A Title for our page
		add(new Label("pageTitle", "Aloha - The Time is: " + new Date()));
		add(new PersonPanel("personPanel"));
	}
}

Well, that certainly got smaller and more concise, didn't it? Now that the panel is in our page component tree, we also make sure it is referenced in the page markup. We add it by id, like any other component. I've basically replaced the list view from yesterday's tip with the panel:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
<html 
	xmlns="http://www.w3.org/1999/xhtml" 
	xmlns:wicket="http://wicket.sourceforge.net/" 
	xml:lang="en" 
	lang="en">  
<head>
	<title wicket:id="pageTitle">[Page Title]</title>
</head>
<body>
	<h1>This is the BEGINNING of the Aloha Page</h1>
		
	<!-- 
		The new panel. Notice that it's just an empty tag that 
		will be filled in by the panel when it is asked to render.
	 -->
	<div wicket:id="personPanel"></div>
	
	<h1>This is the END of the Aloha Page</h1>
</body>
</html>

Intuitively, the panel tag is allowed to be any empty container tag. In the same way that a label's tag will be populated by the label, this new empty tag for the panel will be populated by the panel - the page kind of considers it like a black box - in the same way that a JSP include is a black box. Also notice that I have added a set of headings (h1) to the page to show that the page is responsible for unique content *around* the panel.

Now, compared to the previous tip, our file tree looks like this:

WEB-INF/
|
*---classes/
|   |
|   *---com/javalobby/tnt/wicket/aloha/
|   |   |
|   |   *--- AlohaApplication.class - Application definition
|   |   | 
|   |   *--- AlohaPage.class - AlohaPage component definition
|   |   |
|   |   *--- AlohaPage.html - Associatied HTML markup for aloha page.
|   |   |
|   |   *--- 
PersonPanel.class - PersonPanel component definition

|   |   |
|   |   *--- 
PersonPanel.html - Associated HTML markup for PersonPanel

|   |
|   *--- log4j.properties - log4j properties of your making (or java.util.logging properties)
|
*--- web.xml - The web.xml file.

Notice we only have the two additional files - no extra cruft.

Finally, here is a snapshot of the end result:

This new panel we created can be used in as many places as we want in our application, and all we have to do is add an instance of it to our component tree, and give it an ID based reference in our markup file. No imports and includes, no request/session manipulation, no mess.

There are more advanced features in Wicket, and with panels in particular (such as the ability to provide header contributions to the owning page) - but as I said yesterday we've only scratched the surface of the Wicket feature-base, and hopefully I can cover more and more of these nifty features as time goes on.

Until next time,

R.J. Lorimer
Contributing Editor - rj -at- javalobby.org
Author              - http://www.coffee-bytes.com
Software Consultant - http://www.numbersix.com


thread.rss_message