10 min read

Following are some of the components, we’ll examine in Tapestry 5:

  • The Grid component allows us to display different data in a fairly sophisticated table. We are going to use it to display our collection of celebrities.
  • The BeanEditForm component greatly simplifies creating forms for accepting user input. We shall use it for adding a new celebrity to our collection.
  • The DateField component provides an easy and attractive way to enter or edit the date.
  • The FCKEditor component is a rich text editor, and it is as easy to incorporate into a Tapestry 5 web application, just as a basic TextField is. This is a third party component, and the main point here is to show that using a library of custom components in a Tapestry 5 application requires no extra effort. It is likely that a similar core component will appear in a future version of the framework.

Grid Component

It is possible to display our collection of celebrities with the help of the Loop component. It isn’t difficult, and in many cases, that will be exactly the solution you need for the task at hand. But, as the number of displayed items grow (our collection grows) different problems may arise.

We might not want to display the whole collection on one page, so we’ll need some kind of pagination mechanism and some controls to enable navigation from page to page. Also, it would be convenient to be able to sort celebrities by first name, last name, occupation, and so on. All this can be achieved by adding more controls and more code to finally achieve the result that we want, but a table with pagination and sorted columns is a very common part of a user interface, and recreating it each time wouldn’t be efficient.

Thankfully, the Grid component brings with it plenty of ready to use functionality, and it is very easy to deal with. Open the ShowAll.tml template in an IDE of your choice and remove the Loop component and all its content, together with the surrounding table:

<table width="100%">
<tr t_type="loop" t_source="allCelebrities"
t:value="celebrity">
<td>
<a href="#" t_type="PageLink" t_page="Details"
t:context="celebrity.id">
${celebrity.lastName}
</a>
</td>
<td>${celebrity.firstName}</td>
<td>
<t:output t_format="dateFormat"
t:value="celebrity.dateOfBirth"/>
</td>
<td>${celebrity.occupation}</td>
</tr>
</table>

In place of this code, add the following line:

<t:grid t_source="allCelebrities"/>

Run the application, log in to be able to view the collection, and you should see the following result:

Tapestry 5 Advanced Components

Quite an impressive result for a single short line of code, isn’t it? Not only are our celebrities now displayed in a neatly formatted table, but also, we can sort the collection by clicking on the columns’ headers. Also note that occupation now has only the first character capitalized—much better than the fully capitalized version we had before.

Here, we see the results of some clever guesses on Tapestry’s side. The only required parameter of the Grid component is source, the same as the required parameter of the Loop component. Through this parameter, Grid receives a number of objects of the same class. It takes the first object of this collection and finds out its properties. It tries to create a column for each property, transforming the property’s name for the column’s header (for example, lastName property name gives Last Name column header) and makes some additional sensible adjustments like changing the case of the occupation property values in our example.

All this is quite impressive, but the table, as it is displayed now, has a number of deficiencies:

  • All celebrities are displayed on one page, while we wanted to see how pagination works. This is because the default number of records per page for  Grid component is 25—more than we have in our collection at the moment.
  • The last name of the celebrities does not provide a link to the Details page anymore.
  • It doesn’t make sense to show the Id column.
  • The order of the columns is wrong. It would be more sensible to have the Last Name in the first column, then First Name, and finally the Date of Birth.

By default, to define the display of the order of columns in the table, Tapestry will use the order in which getter methods are defined in the displayed class. In the Celebrity class, the getFirstName method is the first of the getters and so the First Name column will go first, and so on.

There are also some other issues we might want to take care of, but let’s first deal with these four.

Tweaking the Grid

First of all let’s change the number of records per page. Just add the following parameter to the component’s declaration:

<t:grid t_source="allCelebrities" rowsPerPage="5"/>

Run the application, and here is what you should see:

Tapestry 5 Advanced Components

You can now easily page through the records using the attractive pager control that appeared at the bottom of the table. If you would rather have the pager at the top, add another parameter to the Grid declaration:

<t:grid t_source="allCelebrities" rowsPerPage="5"
pagerPosition="top"/>

You can even have two pagers, at the top and at the bottom, by specifying pagerPosition=”both”, or no pagers at all (pagerPosition=”none”). In the latter case however, you will have to provide some custom way of paging through records.

The next enhancement will be a link surrounding the celebrity’s last name and linking to the Details page. We’ll be adding an ActionLink and will need to know which Celebrity to link to, so we have the Grid store using the row parameter. This is how the Grid declaration will look:

<t:grid t_source="allCelebrities" rowsPerPage="5"
row="celebrity"/>

As for the page class, we already have the celebrity property in it. It should have been left from our experiments with the Loop component. It will also be used in exactly the same way as with Loop, while iterating through the objects provided by its source parameter, Grid will assign the object that is used to display the current row to the celebrity property.

The next thing to do is to tell Tapestry that when it comes to the contents of the Last Name column, we do not want Grid to display it in a default way. Instead, we shall provide our own way of displaying the cells of the table that contain the last name. Here is how we do this:

<t:grid t_source="allCelebrities" rowsPerPage="5"
row="celebrity">
<t:parameter name="lastNameCell">
<t:pagelink t_page="details" t_context="celebrity.id">
${celebrity.lastName}
</t:pagelink>
</t:parameter>
</t:grid>

Here, the Grid component contains a special Tapestry element , similar to the one that we used in the previous chapter, inside the If component. As before, it serves to provide an alternative content to display, in this case, the content which will fill in the cells of the Last Name column. How does Tapestry know this? By the name of the element, lastNameCell. The first part of this name, lastName, is the name of one of the properties of the displayed objects. The last part, Cell, tells Tapestry that it is about the content of the table cells displaying the specified property.

Finally, inside , you can see an expansion displaying the name of the current celebrity and surrounding it with the PageLink component that has for its context the ID of the current celebrity.

Run the application, and you should see that we have achieved what we wanted:

Tapestry 5 Advanced Components

Click on the last name of a celebrity, and you should see the Details page with the appropriate details on it.

All that is left now is to remove the unwanted Id column and to change the order of the remaining columns. For this, we’ll use two properties of the Grid—remove and reorder. Modify the component’s definition in the page template to look like this:

<t:grid t_source="celebritySource" rowsPerPage="5"
row="celebrity"
remove="id"
reorder="lastName,firstName,occupation,dateOfBirth">
<t:parameter name="lastNameCell">
<t:pagelink t_page="details" t_context="celebrity.id">
${celebrity.lastName}
</t:pagelink>
</t:parameter>
</t:grid>

Please note that re-ordering doesn’t delete columns. If you omit some columns while specifying their order, they will simply end up last in the table.

Now, if you run the application, you should see that the table with a collection of celebrities is displayed exactly as we wanted:

Tapestry 5 Advanced Components

Changing the Column Titles

Column titles are currently generated by Tapestry automatically. What if we want to have different titles? Say we want to have the title, Birth Date, instead of Date Of Birth.

The easiest and the most efficient way to do this is to use the message catalog, the same one that we used while working with the Select component in the previous chapter. Add the following line to the app.properties file:

dateOfBirth-label=Birth Date

Run the application, and you will see that the column title has changed appropriately. This way, appending -label to the name of the property displayed by the column, you can create the key for a message catalog entry, and thus change the title of any column.

Now you should be able to adjust the Grid component to most of the possible requirements and to display with its help many different kinds of objects. However, one scenario can still raise a problem.

Add an output statement to the getAllCelebrities method in the ShowAll page class, like this:

public List<Celebrity> getAllCelebrities()
{
System.out.println("Getting all celebrities...");
return dataSource.getAllCelebrities();
}

The purpose of this is simply to be aware when the method is called. Run the application, log in, and as soon as the table with celebrities is shown, you will see the output, as follows:

Getting all celebrities...

The Grid component has the allCelebrities property defined as its source, so it invokes the getAllCelebrities method to obtain the content to display. Note however that Grid, after invoking this method, receives a list containing all 15 celebrities in collection, but displays only the first five.

Click on the pager to view the second page—the same output will appear again. Grid requested for the whole collection again, and this time displayed only the second portion of five celebrities from it. Whenever we view another page, the whole collection is requested from the data source, but only one page of data is displayed. This is not too efficient but works for our purpose.

Imagine, however, that our collection contains as many as 10,000 celebrities, and it’s stored in a remote database. Requesting for the whole collection would put a lot of strain on our resources, especially if we are going to have 2,000 pages.

We need to have the ability to request the celebrities, page-by-page—only the first five for the first page, only the second five for the second page and so on. This ability is supported by Tapestry. All we need to do is to provide an implementation of the GridDataSource interface.

Here is a somewhat simplified example of such an implementation.

LEAVE A REPLY

Please enter your comment!
Please enter your name here