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