Sunday, November 22, 2009

Automatic Housing: Tapestry's Scaffolding Components

Tapestry5, if you haven't caught on, is a component-based web application framework. This means that most of its workings is based on discreet components. And the beauty of it is that we can quickly create a front-facing webpage (ala user interface) using Tapestry's "scaffolding" components. We will be using:
  • BeanEditForm - Generates a simple UI for editing the properties of a JavaBean, with the flavor of UI for each property (text field, checkbox, drop down list) determined from the property type (or by other means, such as an annotation), and the order and validation for the properties determined from annotations on the property's getter and setter methods.
  • Grid - A grid presents tabular data. It is a composite component, created in terms of several sub-components. The sub-components are statically wired to the Grid, as it provides access to the data and other models that they need. A Grid may operate inside a form.
We start with rewriting the code in four places; index(tml and java) and another(tml and java). Let's start with the index. Within the index.java source, delete all the code except for anything that has to do with the current time. Do the same with the index.tml source.
 package edu.addressbook.pages;  
 import java.util.Date;  
 import org.apache.tapestry5.annotations.InjectPage;  
 /**  
  * Start page of application AddressBook.  
  */  
 public class Index {  
   public Date getCurrentTime() {  
     return new Date();  
   }  
 }  
After all of that, we add our first scaffolding component which is the grid. The index.tml file should look like this:
 <html t:type="layout" title="AddressBook Index"  
    t:sidebarTitle="Current Time"  
    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"  
    xmlns:p="tapestry:parameter">  
     <!-- Most of the page content, including <head>, <body>, etc. tags, comes from Layout.tml -->  
   <p>${message:greeting}</p>  
   <h1>List of Contacts</h1>  
   <t:grid source="addresses" row="address">  
   </t:grid>  
   <p>  
     <t:pagelink page="another">Add Contact</t:pagelink>  
   </p>  
   <p:sidebar>  
     <p>  
       Just to prove this is live:  
     </p>  
     <p>The current time is: ${currentTime}.</p>  
     <p>  
       [<t:pagelink page="Index">refresh</t:pagelink>]  
     </p>  
   </p:sidebar>  
 </html>  
And the index.java will contain this:
 package edu.addressbook.pages;  
 import edu.addressbook.entities.Address;  
 import edu.addressbook.entities.dao.AddressDAO;  
 import java.util.Date;  
 import java.util.List;  
 import org.apache.tapestry5.ioc.annotations.Inject;  
 /**  
  * Start page of application AddressBook.  
  */  
 public class Index {  
   @Inject  
   private AddressDAO addressDAO;  
   private Address address;  
   public Address getAddress() {  
     return address;  
   }  
   public void setAddress(Address address) {  
     this.address = address;  
   }  
   public List<Address> getAddresses(){  
     return addressDAO.retrieveAll();  
   }  
   public Date getCurrentTime() {  
     return new Date();  
   }  
 }  
When you reload the browser, it should go off without a hitch. Don't worry yet, it shouldn't display anything since there is no data to display. Remember, your database table is empty. We will deal with that. You will notice that there is an "Add Contact" pagelink within the index.tml file. We will use that. Edit the another.tml file to look like this.
 <html t:type="layout" title="Another"  
    t:sidebarTitle="Current Time"  
    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"  
    xmlns:p="tapestry:parameter">  
     <!-- Most of the page content, including <head>, <body>, etc. tags, comes from Layout.tml -->  
   <h1>Create New Address</h1>  
   <t:BeanEditForm t:id="address" />  
   <p:sidebar>  
   </p:sidebar>  
 </html>  
You'll notice that we now are using the BeanEditForm component bind to the address object. Here's the another.java source.
 /*  
  * To change this template, choose Tools | Templates  
  * and open the template in the editor.  
  */  
 package edu.addressbook.pages;  
 import edu.addressbook.entities.Address;  
 import edu.addressbook.entities.dao.AddressDAO;  
 import org.apache.tapestry5.ioc.annotations.Inject;  
 /**  
  *  
  * @author killertilapia  
  */  
 public class Another {  
   private Address address;  
   @Inject  
   private AddressDAO addressDAO;  
   public Address getAddress() {  
     return address;  
   }  
   public void setAddress(Address address) {  
     this.address = address;  
   }  
   Object onSuccess(){  
     addressDAO.add(address);  
     return Index.class;  
   }  
 }  
You should be quick to notice that the create/update button is linked to the onSuccess() method. When the insert is successful it should return to the index page and show the newly added record. Here is a screenshot:


And you should have also noticed that the grid component of ours spills out of the page. It just has too many fields to display. You can exclude that to look like this:
 <h1>List of Contacts</h1>  
   <t:grid source="addresses" row="address" exclude="address1,address2, middlename,landphone">  
   </t:grid>  
The should fix it.

Get the latest code from the repository. And remember happy weaving.