Building Advanced Components For Tapestry Web Applications
Written by Alexander Kolesnikov
Feb 14, 2008 at 02:50 AM
Page 3 of 6
The code is quite simple, we are returning a subset of the existing collection starting
from one index value and ending with the other. In a real-life implementation, we
would probably check whether indexTo is bigger than indexFrom, but here, let's
keep things simple.
Here is one possible implementation of GridDataSource. There are plenty of output
statements in it that do not do anything very useful, but they will allow us to witness
the inner life of Grid and GridDataSet in tandem. Have a look at the code, and then
we'll walk through it step-by-step:
package com.packtpub.celebrities.util;
import com.packtpub.celebrities.data.IDataSource;
import com.packtpub.celebrities.model.Celebrity;
import java.util.List;
import org.apache.tapestry.beaneditor.PropertyModel;
import org.apache.tapestry.grid.GridDataSource;
public class CelebritySource implements GridDataSource
{
private IDataSource dataSource;
private List<Celebrity> selection;
private int indexFrom;
public CelebritySource(IDataSource ds)
{
this.dataSource = ds;
}
public int getAvailableRows()
{
return dataSource.getAllCelebrities().size();
}
public void prepare(int indexFrom, int indexTo,
PropertyModel propertyModel, boolean ascending)
{
System.out.println("Preparing selection.");
System.out.println("Index from " + indexFrom + " to " + indexTo);
String propertyName = propertyModel == null?
null : propertyModel.getPropertyName();
System.out.println("Property name is: " + propertyName);
System.out.println("Sorting order ascending: " + ascending);
selection = dataSource.getRange(indexFrom, indexTo);
this.indexFrom = indexFrom;
}
public Object getRowValue(int i)
{
System.out.println("Getting value for row " + i);
return selection.get(i - this.indexFrom);
}
public Class getRowType()
{
return Celebrity.class;
}
}
First of all, when creating an instance of CelebritySource, we are passing an
implementation of IDataSource that will imitate an actual data source to its
constructor. In real life this could be some Data Access Object.
The GridDataSource interface that we implemented contains four methods:
getAvailableRows(), prepare(),getRowValue(), and getRowType(). The
simplest of them is getRowType(). It simply reports which type of objects are served
by this implementation of GridDataSource.
The getAvailableRows method returns the total number of entities available in the
data source (this is needed to know the number of pages and to construct the pager
properly). In our case, we are simply returning the size of a collection. In real life, this
method could contain a request to the database that would return the total available
number of records in a search result, without actually returning all those records.
If you insert an output statement into this method, you will notice that it is invoked
by Grid several times, even while a single page of the table is displayed. You will not
want to call the database that many times, so you will need to include some logic to
cache the result returned by this method, and update it only when necessary. But,
again, we are looking at the principles here, so let's keep everything simple.
The prepare method does the main job of requesting the database and obtaining
a subset of entities from it to be displayed by the current page of the table.
The
subset is limited by the first two parameters—indexFrom and indexTo, which are
the indexes of the first and the last entities to be returned. They might be used in a
SELECT statement which would command the database to select all the entities and
then limit the selection in one way or another, depending on the SQL dialect.
The third parameter of this method, propertyModel, is used to de fine the column by
which the result should be sorted.
Again, we could use this parameter in a SELECTstatement, but here we are simply outputting the name of the property to see what
the Grid has passed to the method.
Finally, the ascending parameter could be used to de fine the order in which the
results should be sorted when speaking to the database, but we are just outputting
its value.
The last of the four methods, getRowValue(), returns the entity requested by Grid
using its index as a parameter. You will see how all this works soon.
To make use of the created CelebritySource, add the following method to the
ShowAll page class:
public GridDataSource getCelebritySource()
{
return new CelebritySource(dataSource);
}
Then change the source parameter of the Grid component in ShowAll.tml template:
Run the application. Log in to view the ShowAll page, and as soon as the table with
celebrities is displayed, you should see the following output: Preparing selection.
Index from 0 to 4
Property name is: null
Sorting order ascending: true
Getting value for row 0
Getting value for row 1
Getting value for row 2
Getting value for row 3
Getting value for row 4
From this you can see that to display the first page of results, the Grid component
invoked the methods of the GridDataSource implementation provided by its source
parameter in a certain succession. The output shows that the prepare method was
invoked with the indexFrom parameter set to 0, and the indexTo parameter set to
4. These are indexes of the first five celebrities in collection. The propertyModel
parameter was null, so no speci fic sorting was requested. Finally, the getRowValue
method was invoked five times to obtain an object to be displayed by each of the five
rows in the table.
Click on the pager to view the second page of results and the result will be similar,
only the indices will be different: Preparing selection.
Index from 5 to 9
Property name is: null
Sorting order ascending: true
Getting value for row 5
Getting value for row 6
Getting value for row 7
Getting value for row 8
Getting value for row 9
Click on the header of one of the columns, and you will see the change in the
property name passed to the prepare method:
Property name is: lastName
Sorting order ascending: true
Now the data source will be requested to sort the result by last name. Of course, no
sorting will take place in our simpli fied example as we are simply outputting the
name of the property and not using it in an actual request to a database.
Click on the same column once again, and this time you will see that the order of
sorting is changed:
Property name is: lastName
Sorting order ascending: false
You can see from this example that Tapestry allows us to de fine precisely how
a database (or other data source) should be called, and we can request data,
page-by-page by creating an implementation of GridDataSource interface. The
Grid component will then invoke the methods of this interface and display the
information returned by them appropriately.
Next, we are going to see another advanced component, BeanEditForm. It is
somewhat similar to Grid in that it also can make use of BeanModel, and its
con figuration is pretty similar too.
BeanEditForm Component
Our current collection of celebrities is tiny, and it would be a good idea to provide
in the application functionality for adding new celebrities. Let's begin by adding a template and a page class for a new page named AddCelebrity. Add to the page
class a single persistent property named celebrity, so that its code looks like this:
package com.packtpub.celebrities.pages;
import com.packtpub.celebrities.model.Celebrity;
import org.apache.tapestry.annotations.Persist;
public class AddCelebrity
{
@Persist
private Celebrity celebrity;
public Celebrity getCelebrity()
{
return celebrity;
}
public void setCelebrity(Celebrity celebrity)
{
this.celebrity = celebrity;
}
}
In the page template, declare one single component of type BeanEditForm and let its
id be the same as the name of the property of the page class, in our case, celebrity:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head>
<title>Celebrity Collector: Adding New
Celebrity</title>
</head>
<body>
<h1>Adding New Celebrity</h1>
<t:beaneditform t:id="celebrity"/>
</body>
</html>
We also need to somehow connect the new page to the rest of the application. For
instance, we could add this new PageLink component somewhere at the bottom of
the ShowAll page:
<a href="#" t:type="PageLink" t:page="AddCelebrity">Add new
Celebrity
</a><br/>
<a href="#" t:type="PageLink" t:page="Start">
Back to the Start Page
</a>
Page 3 Of 6 -
Advanced Components from the book "Tapestry 5 - Building Web Applications"
Copyright 2004 to 2008 Rightrix Solutions. All rights reserved. All product names are trademarks of their respective companies. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Rightrix Solutions and IndicThreads.com are independent of Sun Microsystems, Inc.
Views expressed at IndicThreads.com reflect the views of the authors alone, and do not necessarily reflect those of IndicThreads.com. IndicThreads.com and it's authors are not responsible for reader comments and opinions.