simplifies web development and makes it fun. Are you bored of going through countless pages of theory to find out how to get your web development done? With this book in hand, you don’t need to go through hundreds of pages to figure out how you will actually build a web application. You will get practical solutions to your common everyday development tasks to pace up your development activities.
Apache Wicket Cookbook provides you with information that gets your problems solved quickly without beating around the bush. This book is perfect for you if you are ready to take the next step from tutorials and step into the practical world. It will take you beyond the basics of using Apache Wicket and show you how to leverage Wicket‘s advanced features to create simpler and more maintainable solutions to what at first may seem complex problems.
You will learn how to integrate with client-side technologies such as JavaScript libraries or Flash components, which will help you to build your application faster. You will discover how to use Wicket paradigms to factor out commonly used code into custom Components, which will reduce the maintenance cost of your application, and how to leverage the existing Wicket Components to make your own code simpler.
A straightforward Cookbook with over 70 highly focused practical recipes to make your web application development easier with the Wicket web framework.
What This Book Covers
Chapter 1, Validating and Converting User Input: This chapter is all about form
validation and input conversion. Learn how to enforce constraints on the inputs and how to convert string inputs into other types so the rest of your code can be type-safe.
Chapter 2, Getting Down and Dirty with Forms and Form Components: In this chapter, we will examine some of the in-depth form patterns such as preventing double submits and refactoring form components to make them more reusable.
Chapter 3, Making Forms Presentable: This chapter is all about making your forms look good. Here we will see how to change presentation of form components and labels to give the user better feedback and how to package that code in a reusable manner.
Chapter 4, Taking your Application Abroad: This chapter is about internationalization. Here you will learn how to take advantage of the many features Wicket offers to make your application a good international citizen.
Chapter 5, Displaying Data Using DataTable: No web application is complete without a page that has a table of data. In this chapter we will learn how to make great use of Wicket‘s DataTable components to make displaying awesome tables a snap.
Chapter 6, Enhancing your UI with Tabs and Borders: In this chapter we will take a look at some of the more advanced use cases of using TabbedPanel and Border components.
Chapter 7, Deeper into Ajax: Are you ready to take your Ajax knowledge past the basics? This chapter will show you how.
Chapter 8, Visualizing Data with Charts: Build an awesome Wicket component that can display charts using OpenFlashCharts; and as a bonus, learn how to integrate Wicket with client-side components built with Flash or JavaScript.
Chapter 9, Building Dynamic and Rich UI: In this chapter, we will learn how to take
advantage of Wicket‘s dynamic component hierarchy to easily manipulate the user
interface of our applications.
Chapter 10, Securing your Application: Ever wish you had a read recipe to follow for
how to integrate services such as OpenID into your application? Have you spent nights thinking of ways to assign roles and permissions to individual Wicket components easily? This chapter covers all that and more.
Chapter 11, Integrating Wicket with Middleware: Wicket tries to do one thing, but do it very well – allow you to build awesome user interfaces. But, web applications are made up of more than just interfaces. In this chapter, we will learn how to integrate Wicket with middleware such as Spring, CDI, and JPA.
Chapter 12, General Wicket Patterns: This chapter is like a small encyclopedia of recipes for common but non-trivial use cases that make you scratch your head.
1 2 3 4 | Chapter 12, General Wicket Patterns is not present in the book but is available as a free download from the following link: http://www.packtpub.com/sites/default/files/downl oads/1605_Chapter12.pdf |
- Sorting
- Filtering
- Making cells clickable
- Making rows selectable with checkboxes
- Exporting data to CSV
Introduction
It is hard to fi nd a web application that does not have a single table that presents the user with some data. Building these DataTables, although not very diffi cult, can be a daunting task because each of these tables must often support paging, sorting, fi ltering, and so on. Wicket ships with a very powerful component called the DataTable that makes implementing all these features simple and elegant. Because Wicket is component-oriented, once implemented, these features can be easily reused across multiple DataTable deployments. In this chapter, we will see how to implement the features mentioned previously using the DataTable and the infrastructure it provides.
Sorting
A common requirement, when displaying tabular data, is to allow users to sort it by clicking the table headers. Click a header once and the data is sorted on that column in ascending order; click it again, and the data is sorted in the descending order.
In this recipe, we will see how to implement such a behavior when displaying data using a DataTable component. We will build a simple table that will look much like a phone book and will allow the sorting of data on the name and e-mail columns:
Getting ready
Begin by creating a page that will list contacts using the DataTable, but without sorting:
- Create Contact bean:
1 2 3 4
Contact.java public class Contact implements Serializable { public String name, email, phone; // getters, setters, constructors2. - Create the page that will list the contacts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
HomePage.html <html> <body> <table wicket:id="contacts" class="contacts"></table> </body> </html> HomePage.java public class HomePage extends WebPage { private static List<Contact> contacts = Arrays.asList( new Contact("Homer Simpson", "homer@fox.com", "555-1211"), new Contact("Charles Burns", "cmb@fox.com", "555-5322"), new Contact("Ned Flanders", "green@fox.com", "555-9732")); public HomePage(final PageParameters parameters) { // sample code adds a DataTable and a data providert hat uses the contacts list created above } }
How to do it…
- Enable sorting by letting DataTable columns know they can be sorted by using a constructor that takes the sort data parameter:
1 2 3 4 5 6 7 8 9
HomePage.java List<IColumn<Contact>> columns = new ArrayList<IColumn<Contact>>(); columns.add(new PropertyColumn<Contact>(Model.of("Name"), "name","name")); columns.add(new PropertyColumn<Contact>(Model.of("Email"), "email", "email")); columns.add(new PropertyColumn<Contact>(Model.of("Phone"), "phone")); - Implement sorting by modifying the data provider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
private static class ContactsProvider extends SortableDataProvider<Contact> { public ContactsProvider() { setSort("name", true); } public Iterator<? extends Contact> iterator(int first, int count) { List<Contact> data = new ArrayList<Contact>(contacts); Collections.sort(data, new Comparator<Contact>() { public int compare(Contact o1, Contact o2) { int dir = getSort().isAscending() ? 1 : -1; if ("name".equals(getSort().getProperty())) { return dir * (o1.name.compareTo(o2.name)); } else { return dir * (o1.email.compareTo(o2.email)); } } }); return data.subList(first, Math.min(first + count, data.size())).iterator(); } public int size() { return contacts.size(); } public IModel<Contact> model(Contact object) { return Model.of(object); } }
How it works…
DataTable supports sorting out of the box. Any column with the IColumn#getSortProperty() method that returns a non-null value is treated as a sortable column and Wicket makes its header clickable. When a header of a sortable column is clicked Wicket will pass the value of IColumn#getSortProperty to the data provider which should use this value to sort the data. In order to know about the sorting information the data provider must implement the ISortableDataProvider interface; Wicket provides the default SortableDataProvider implementation which is commonly used to implement sort-capable data providers. DataTable will take care of details such as multiple clicks to the same column resulting in change of sorting direction, so on.
Let’s examine how to implement sorting in practice. In step 1 and 2, we have implemented a basic DataTable that cannot yet sort data. Even though the data provider we have implemented already extends a SortableDataProvider, it does not yet take advantage of any sort information that may be passed to it.
We start building support for sorting by enabling it on the columns, in our case the name and the email columns:
1 2 3 4 5 6 | List<IColumn<Contact>> columns = new ArrayList<IColumn<Contact>>();
columns.add(new PropertyColumn<Contact>(Model.of("Name"), "name",
"name"));
columns.add(new PropertyColumn<Contact>(Model.of("Email"), "email",
"email"));
columns.add(new PropertyColumn<Contact>(Model.of("Phone"), "phone")); |
We enable sorting on the columns by using the three-argument constructor of the
PropertyColumn, with the second argument being the “sort data”. Whenever a DataTable column with sorting enabled is clicked, the data provider will be given the value of the “sort data”. In the example, only the name and e-mail columns have sorting enabled with the sort data defi ned as a string with values “name” and “e-mail” respectively.
Now, let’s implement sorting by making our data provider implementation sort-aware. Since our data provider already extends a provider that implements ISortableDataProvider we only need to take advantage of the sort information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public Iterator<? extends Contact> iterator(int first, int count) {
List<Contact> data = new ArrayList<Contact>(contacts);
Collections.sort(data, new Comparator<Contact>() {
public int compare(Contact o1, Contact o2) {
int dir = getSort().isAscending() ? 1 : -1;
if ("name".equals(getSort().getProperty())) {
return dir * (o1.name.compareTo(o2.name));
} else {
return dir * (o1.email.compareTo(o2.email));
}
}
});
return data.subList(first,
Math.min(first + count, data.size())).iterator();
} |
First we copy the data into a new list which we can sort as needed and then we sort based on the sort data and direction provided. The value returned by getSort().getProperty() is the same sort data values we have defi ned previously when creating columns.
The only remaining task is to defi ne a default sort which will be used when the table is rendered before the user clicks any header of a sortable column. We do this in the constructor of our data provider:
1 2 3 | public ContactsProvider() {
setSort("name", true);
} |
There’s more…
DataTable gives us a lot out of the box; in this section we see how to add some
usability enhancements.
Adding sort direction indicators via CSS
DataTable is nice enough to decorate sortable <th> elements with sort-related CSS classes out of the box. This makes it trivial to implement sort direction indicators as shown in the following screenshot:
1 2 3 4 5 6 7 8 | table tr th { background-position: right; background-repeat:no-repeat;
}
table tr th.wicket_orderDown {
background-image: url(images/arrow_down.png); }
table tr th.wicket_orderUp {
background-image: url(images/arrow_up.png); }
table tr th.wicket_orderNone {
background-image: url(images/arrow_off.png); |
Filtering
One of the more common use cases for using the DataTable component is to display a large data set which often needs to be paged. But, navigating large data sets can be cumbersome even with the best of paging interfaces. Most users prefer to search rather than page to fi nd the information. In this recipe, we will see how to make a DataTable searchable . We will implement a simple quick-search type form which will filter records in the DataTable, shown as follows:
Getting ready
We begin by creating the page without filtering.
Create the contact bean. Refer to Contact.java in the code bundle.
Create the page to list contacts:
1 2 3 4 5 6 7 8 9 10 11 12 13 | HomePage.java
// for markup refer to HomePage.html in the code bundle
public class HomePage extends WebPage {
private String filter;
public HomePage(final PageParameters parameters) {
// sample code adds DataTable just like the first recipe
Form<?> form = new Form<Void>("form");
add(form);
form.add(new TextField<String>("filter", new
PropertyModel<String>(
this, "filter")));
}
} |
How to do it…
Modify the data provider to fi lter data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | HomePage.java
private class ContactsProvider
extends SortableDataProvider<Contact> {
private transient List<Contact> filtered;
private List<Contact> getFiltered() {
if (filtered == null) {
filtered = filter();
}
return filtered;
}
private List<Contact> filter() {
List<Contact> filtered=new ArrayList<Contact>(contacts);
if (filter != null) {
String upper = filter.toUpperCase();
Iterator<Contact> it = filtered.iterator();
while (it.hasNext()) {
Contact contact = it.next();
if (contact.name.toUpperCase().indexOf(upper) < 0
&& contact.email.toUpperCase().indexOf(upper) < 0) {
it.remove();
}
}
}
return filtered;
}
@Override
public void detach() {
filtered = null;
super.detach();
}
public Iterator<? extends Contact> iterator(int first, int count) {
return getFiltered()
.subList(
first,
Math.min(first + count, getFiltered().size()))
.iterator();
}
public int size() {
return getFiltered().size();
}
public IModel<Contact> model(Contact object) {
return Model.of(object);
}
} |
How it works…
In step 1 and 2, we have implemented both the DataTable to display contacts as well as the form used to fi lter them. In order to enable fi ltering, we must connect our form to the data provider.
As we are working with an in-memory list of contacts, let’s implement a method in data provider which will return a list of contacts that matches the fi lter selected in the form. The form will store the fi lter in the HomePage’s filter fi eld, courtesy of the following property model:
1 2 | form.add(new TextField<String>("filter",
new PropertyModel<String>(this, "filter"))); |
We can use this field to fi lter the contact list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | private class ContactsProvider extends SortableDataProvider<Contact> {
private List<Contact> filter() {
List<Contact> filtered=new ArrayList<Contact>(contacts);
if (filter != null) {
String upper = filter.toUpperCase();
Iterator<Contact> it = filtered.iterator();
while (it.hasNext()) {
Contact contact = it.next();
if (contact.name.toUpperCase().indexOf(upper) < 0
&& contact.email.toUpperCase().indexOf(upper) < 0)
{
it.remove();
}
}
}
return filtered;
}
} |
The fi lter method itself needs no explanation. As both IDataProvider#iterator() and
IDataProvider#size() need to access the fi ltered list, lets cache it for the duration of the request so we are not calculating it needlessly:
1 2 3 4 5 6 7 8 9 10 11 12 13 | private class ContactsProvider extends SortableDataProvider<Contact> {
private transient List<Contact> filtered;
private List<Contact> getFiltered() {
if (filtered == null) {
filtered = filter();
}
return filtered;
}
public void detach() {
filtered = null;
super.detach();
}
} |
In the preceding code, we cache the result of the ContactsProvider#filter() method until the IDataProvider#detach() method is called. This method, like Imodel#detach() and Component#detach() will be called at the end of the request,
at which point we no longer need the cached value.
1 2 3 4 5 6 | Notice that we declared the ContactsProvider#filtered fi eld as transient. This is because this fi eld serves only as a cache and we do not need to serialize it. Although not strictly necessary, as it is cleared in the detach() method, this is a nice safeguard in case we forget to clear the cache in some scenario. It also helps clarify the intent of the fi eld by making it obvious that the value of this fi eld is not persisted across requests. |
Now that we have an effi cient method of accessing the fi ltered list we use it to implement the rest of the data provider:
1 2 3 4 5 6 7 8 9 | private class ContactsProvider extends SortableDataProvider<Contact> {
public Iterator<? extends Contact> iterator(int first, int count) {
return getFiltered().subList(first,
Math.min(first + count, getFiltered().size())).iterator();
}
public int size() {
return getFiltered().size();
}
} |
There’s more…
Unlike our example, which stores its data in a memory list, most real world applications access data stored in a database. In the next section, we will see how to support sorting of such data.
Sorting database data
Below is a sample implementation of the data provider that fi lters data coming from a database:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private class DatabaseContactsProvider extends
SortableDataProvider<Contact> {
public Iterator<? extends Contact> iterator(int first, int
count) {
return getApplication().getDatabase().query(first, count,
getSort().getProperty(), getSort().isAscending());
}
public int size() {
return getApplication().getDatabase().count(getSort().
getProperty(), getSort().isAscending());
}
public IModel<Contact> model(Contact object) {
return new EntityModel<Contact>(object);
}
} |
As we can see from the highlighted lines, the data provider delegates the fi ltering and sorting to the database by passing in all necessary parameters for the database to be able to build a proper query.









April 30, 2011
Java