You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2008/01/30 19:34:05 UTC
svn commit: r616835 - in /tapestry/tapestry5/trunk/tapestry-core/src/main:
java/org/apache/tapestry/corelib/base/
java/org/apache/tapestry/corelib/components/
java/org/apache/tapestry/services/
resources/org/apache/tapestry/corelib/components/
Author: hlship
Date: Wed Jan 30 10:34:03 2008
New Revision: 616835
URL: http://svn.apache.org/viewvc?rev=616835&view=rev
Log:
TAPESTRY-1853: Create a guide to using the Grid component
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Grid.xdoc
Removed:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/base/AbstractComponentActionLink.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Grid.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PropertyConduitSource.java
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/ActionLink.xdoc
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Grid.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Grid.java?rev=616835&r1=616834&r2=616835&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Grid.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Grid.java Wed Jan 30 10:34:03 2008
@@ -27,7 +27,6 @@
import org.apache.tapestry.internal.beaneditor.BeanModelUtils;
import org.apache.tapestry.internal.bindings.AbstractBinding;
import org.apache.tapestry.ioc.annotations.Inject;
-import org.apache.tapestry.ioc.services.TypeCoercer;
import org.apache.tapestry.services.BeanModelSource;
import org.apache.tapestry.services.FormSupport;
@@ -48,11 +47,11 @@
{
/**
* The source of data for the Grid to display. This will usually be a List or array but can also be an explicit
- * {@link GridDataSource}. For Lists and Arrays, a GridDataSource is created automatically as a wrapper around the
- * underlying List.
+ * {@link GridDataSource}. For Lists and object arrays, a GridDataSource is created automatically as a wrapper
+ * around the underlying List.
*/
@Parameter(required = true)
- private Object _source;
+ private GridDataSource _source;
/**
* The number of rows of data displayed on each page. If there are more rows than will fit, the Grid will divide up
@@ -69,15 +68,6 @@
@Parameter(value = "top", defaultPrefix = "literal")
private GridPagerPosition _pagerPosition;
- @Persist
- private int _currentPage = 1;
-
- @Persist
- private String _sortColumnId;
-
- @Persist
- private boolean _sortAscending = true;
-
/**
* Used to store the current object being rendered (for the current row). This is used when parameter blocks are
* provided to override the default cell renderer for a particular column ... the components within the block can
@@ -127,18 +117,14 @@
@Parameter
private boolean _lean;
- @Inject
- private ComponentResources _resources;
-
- @Inject
- private BeanModelSource _modelSource;
-
- @Inject
- private TypeCoercer _typeCoercer;
-
- // Transformed version of the source parameter.
-
- private GridDataSource _dataSource;
+ /**
+ * If true and the Loop is enclosed by a Form, then the normal state persisting logic is turned off. Defaults to
+ * false, enabling state persisting within Forms. If a Grid is present for some reason within a Form, but does not
+ * contain any form control components (such as {@link TextField}), then binding volatile to false will reduce the
+ * amount of client-side state that must be persisted.
+ */
+ @Parameter
+ private boolean _volatile;
/**
* The CSS class for the tr element for each data row. This can be used to highlight particular rows, or cycle
@@ -147,6 +133,22 @@
@Parameter(cache = false)
private String _rowClass;
+ @Persist
+ private int _currentPage = 1;
+
+ @Persist
+ private String _sortColumnId;
+
+ @Persist
+ private boolean _sortAscending = true;
+
+ @Inject
+ private ComponentResources _resources;
+
+ @Inject
+ private BeanModelSource _modelSource;
+
+
@SuppressWarnings("unused")
@Component(parameters = {"sortColumnId=sortColumnId", "sortAscending=sortAscending", "lean=inherit:lean"})
private GridColumns _columns;
@@ -167,14 +169,6 @@
@Component(parameters = "to=pagerBottom")
private Delegate _pagerBottom;
- /**
- * If true and the Loop is enclosed by a Form, then the normal state persisting logic is turned off. Defaults to
- * false, enabling state saving persisting within Forms. If a Grid is present for some reason within a Form, but
- * does not contain any form control components (such as {@link TextField}), then binding volatile to false will
- * reduce the amount of client-side state that must be persisted.
- */
- @Parameter
- private boolean _volatile;
@Environmental(false)
private FormSupport _formSupport;
@@ -190,9 +184,10 @@
{
// Get the default row type from the data source
- Class rowType = _dataSource.getRowType();
+ Class rowType = _source.getRowType();
- if (rowType == null) throw new RuntimeException("xxx -- no source to determine list type from");
+ if (rowType == null) throw new RuntimeException(
+ "Unable to determine the bean type for rows from the GridDataSource. You should bind the model parameter explicitly.");
// Properties do not have to be read/write
@@ -229,20 +224,18 @@
setupDataSource();
- return _dataSource.getAvailableRows() == 0 ? _empty : null;
+ return _source.getAvailableRows() == 0 ? _empty : null;
}
void setupDataSource()
{
- _dataSource = _typeCoercer.coerce(_source, GridDataSource.class);
-
if (_remove != null) BeanModelUtils.remove(_model, _remove);
if (_reorder != null) BeanModelUtils.reorder(_model, _reorder);
// If there's no rows, display the empty block placeholder.
- int availableRows = _dataSource.getAvailableRows();
+ int availableRows = _source.getAvailableRows();
if (availableRows == 0) return;
@@ -265,7 +258,7 @@
int startIndex = (_currentPage - 1) * _rowsPerPage;
int endIndex = Math.min(startIndex + _rowsPerPage - 1, availableRows - 1);
- _dataSource.prepare(startIndex, endIndex, sortModel, _sortAscending);
+ _source.prepare(startIndex, endIndex, sortModel, _sortAscending);
}
Object beginRender()
@@ -273,7 +266,7 @@
// Skip rendering of component (template, body, etc.) when there's nothing to display.
// The empty placeholder will already have rendered.
- if (_dataSource.getAvailableRows() == 0) return false;
+ if (_source.getAvailableRows() == 0) return false;
return null;
}
@@ -285,7 +278,7 @@
public GridDataSource getDataSource()
{
- return _dataSource;
+ return _source;
}
public String getRowClass()
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java?rev=616835&r1=616834&r2=616835&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/BeanModelSource.java Wed Jan 30 10:34:03 2008
@@ -24,6 +24,8 @@
* <p/>
* BeanModels are the basis for the {@link org.apache.tapestry.corelib.components.BeanEditor} and {@link
* org.apache.tapestry.corelib.components.Grid} comopnents.
+ *
+ * @see org.apache.tapestry.services.PropertyConduitSource
*/
public interface BeanModelSource
{
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PropertyConduitSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PropertyConduitSource.java?rev=616835&r1=616834&r2=616835&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PropertyConduitSource.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/PropertyConduitSource.java Wed Jan 30 10:34:03 2008
@@ -18,18 +18,17 @@
import org.apache.tapestry.internal.bindings.PropBindingFactory;
/**
- * A source for {@link PropertyConduit}s, which can be thought of as a compiled property path
- * expression. PropertyConduits are the basis of the "prop:" binding factory, thus this service
- * defines the expression format used by the {@link PropBindingFactory}.
+ * A source for {@link PropertyConduit}s, which can be thought of as a compiled property path expression.
+ * PropertyConduits are the basis of the "prop:" binding factory, thus this service defines the expression format used
+ * by the {@link PropBindingFactory}.
* <p/>
- * The expression consist of one or more terms, seperated by periods. Each term may be either the
- * name of a JavaBean property, or the name of a method (a method that takes no parameters). Method
- * names are distinguished from property names by appending empty parens. Using a method term as the
- * final term will make the expression read-only.
+ * The expression consist of one or more terms, seperated by periods. Each term may be either the name of a JavaBean
+ * property, or the name of a method (a method that takes no parameters). Method names are distinguished from property
+ * names by appending empty parens. Using a method term as the final term will make the expression read-only.
* <p/>
- * Alternately, the seperator between property names may be "?." (i.e., "user?.name"). This allows
- * an "if not null" connection: if the term is null, then the expression evaluates to null, otherwise,
- * the expression continues to the next property. The helps avoid NullPointerExpressions.
+ * Alternately, the seperator between property names may be "?." (i.e., "user?.name"). This allows an "if not null"
+ * connection: if the term is null, then the expression evaluates to null, otherwise, the expression continues to the
+ * next property. The helps avoid NullPointerExceptions.
*/
public interface PropertyConduitSource
{
@@ -38,8 +37,7 @@
*
* @param rootClass the class of the root object to which the expression is applied
* @param expression
- * @return RuntimeException if the expression is invalid (poorly formed, references non-existent
- * properties, etc.)
+ * @return RuntimeException if the expression is invalid (poorly formed, references non-existent properties, etc.)
*/
PropertyConduit create(Class rootClass, String expression);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/ActionLink.xdoc
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/ActionLink.xdoc?rev=616835&r1=616834&r2=616835&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/ActionLink.xdoc (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/ActionLink.xdoc Wed Jan 30 10:34:03 2008
@@ -42,7 +42,7 @@
<p>
We store the account's id as
<em>event context</em>
- inside the URL. The accounts id will
+ inside the URL. The account's id will
be part of the URL and will be accessible when the event request is later triggered.
</p>
@@ -103,8 +103,8 @@
<p>
Rarely, you might need to pass more information in the context. For example, perhaps
account id is not enought to uniquely identify the Account object in question - hypothetically,
- you may need to include a componany id as well as the account id. You can
- build an object array:
+ you may need to include a company id as well as the account id. You can
+ build an object array to contain both values:
</p>
<source><![CDATA[
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Grid.xdoc
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Grid.xdoc?rev=616835&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Grid.xdoc (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Grid.xdoc Wed Jan 30 10:34:03 2008
@@ -0,0 +1,232 @@
+<document>
+ <body>
+ <section name="Simple Example">
+
+ <p>
+ The Grid component is closely related to the BeanEditor component; they are both based on the same
+ underlying concept and share quite a bit of code.
+ </p>
+
+ <p>In this example, we'll display a list of users.</p>
+
+ <subsection name="User.java">
+ <source><![CDATA[
+public class User
+{
+ private long _id;
+
+ private String _firstName;
+
+ private String _lastName;
+
+ private int _age;
+
+ public long getId() { return _id; }
+
+ @NonVisual
+ public void setId(long id) { _id = id; }
+
+ public String getFirstName() { return _firstName; }
+
+ public void setFirstName(String firstName) { _firstName = firstName; }
+
+ public String getLastName() { return _lastName; }
+
+ public void setLastName(String lastName) { _lastName = lastName; }
+
+ public int getAge() { return _age; }
+
+ public void setAge(int age) { _age = age; }
+}]]></source>
+
+ <p>The @NonVisual annotation prevents the id from being displayed.</p>
+
+ </subsection>
+
+ <subsection name="UserList.tml">
+
+ <p>
+ We want to make the user's last name a clickable link to a detail page for the user.
+ </p>
+
+
+ <source><![CDATA[
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+ <body>
+ <h1>List Users</h1>
+
+ <t:grid source="users" row="user">
+ <t:parameter name="lastnamecell">
+ <t:pagelink page="viewuser" context="user.id">${user.lastname}</t:pagelink>
+ </t:parameter>
+ </t:grid>
+ </body>
+</html>
+]]></source>
+
+
+ <p>
+ The parameter name
+ <code><em>property</em>cell
+ </code>
+ is used
+ to override the rendering of cells for one property. As usual, case is ignored. Here we
+ use a PageLink component to link to a ViewUser page, passing the id of the user as
+ activation context for the target page.
+ </p>
+
+ <p>
+ For the block to know what is being rendered, we bind the row parameter of the Grid
+ to the user property of the page. The Grid will keep updating this property
+ just before it renders each row (using its own internal renderers, or the ones
+ provided as parameters).
+ </p>
+
+ <p>
+ Overriding of headers, the clickable links at the top of columns, is not yet implemented.
+ </p>
+
+ </subsection>
+
+ <subsection name="UserList.java">
+ <source><![CDATA[
+public class UserList
+{
+ @Inject
+ private UserDAO _userDAO;
+
+ private User _user;
+
+ public List<User> getUsers() { return _userDAO.findAll(); }
+
+ public void setUser(User user) { _user = user; }
+
+ public User getUser() { return _user; }
+}]]></source>
+
+ </subsection>
+
+ <p>
+ The UserList class exists to provide access to the UserDAO service, and to act as a holder
+ for the user property, needed when the Grid is rendering.
+ </p>
+
+ </section>
+
+ <section name="Adding Columns Example">
+
+ <p>
+ Commonly, you may want to add a column to the Grid to support a computed property, or as a placeholder
+ for an action. We'll do the latter, adding a column for deleting a user.
+ </p>
+
+ <subsection name="UserList.tml">
+
+ <p>
+ We want to make the user's last name a clickable link to a detail page for the user.
+ </p>
+
+
+ <source><![CDATA[
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+ <body>
+ <h1>List Users</h1>
+
+ <t:grid source="users" model="model" row="user">
+ <t:parameter name="lastnamecell">
+ <t:pagelink page="viewuser" context="user.id">${user.lastname}</t:pagelink>
+ </t:parameter>
+ <t:parameter name="deletecell">
+ <t:actionlink t:id="delete" context="user.id">Delete</t:actionlink>
+ </t:parameter>
+ </t:grid>
+ </body>
+</html>
+]]></source>
+
+ <p>
+ We now explicitly provide a model parameter. In addition, a block
+ for the "delete" property has been added that includes an ActionLink
+ used to delete the user for the current row. This property is a
+ <em>virtual</em>
+ property because it doesn't correspond to a property
+ of the data object, User.
+ </p>
+
+ </subsection>
+
+ <subsection name="UserList.java">
+ <source><![CDATA[
+public class UserList
+{
+ @Inject
+ private UserDAO _userDAO;
+
+ @Inject
+ private BeanModelSource _beanModelSource;
+
+ @Inject
+ private ComponentResources _resources;
+
+ private final BeanModel _model;
+
+ {
+ _model = _source.create(User.class, true, _resources);
+
+ _model.add("delete", null);
+ }
+
+ private User _user;
+
+ public List<User> getUsers() { return _userDAO.findAll(); }
+
+ public void setUser(User user) { _user = user; }
+
+ public User getUser() { return _user; }
+
+ public BeanModel getModel() { return _model; }
+
+ void onActionFromDelete(long userId)
+ {
+ _userDAO.remove(userId);
+ }
+}]]></source>
+
+ <p>
+ Here we create the model using the BeanModelSource service.
+ We then customize it, adding a new property, "delete". The null
+ indicates that this is a virtual property, with no real data behind it.
+ </p>
+
+ </subsection>
+
+ <subsection name="UserList.properties">
+ <source><![CDATA[
+delete-label=Delete user?]]></source>
+
+ <p>
+ The normal column title for the "delete" property would be "Delete". Using the
+ page's message catalog we can override that.
+ </p>
+ </subsection>
+
+
+ </section>
+
+
+ <section name="Notes">
+
+ <p>
+ Tapestry does a lot of work to help you with the source parameter. The parameter type
+ is GridDataSource, but Tapestry has built-in coercions from
+ Object[] and List. In more complicated cases, such as very large
+ queries against a database, you will want to provide your own implementation
+ of GridDataSource, to minimimze the sizes of queries and result sets.
+ </p>
+
+
+ </section>
+
+
+ </body>
+</document>
\ No newline at end of file