...
Tapestry treats public fields as if they were JavaBeans properties; since the Address object is just "dumb data", there's no need to get carried away writing getters and setters. Instead, we'll define an entity that is all public fields:
Code Block | ||||
---|---|---|---|---|
| ||||
package com.example.tutorial.entities;
import com.example.tutorial.data.Honorific;
public class Address
{
public Honorific honorific;
public String firstName;
public String lastName;
public String street1;
public String street2;
public String city;
public String state;
public String zip;
public String email;
public String phone;
}
|
We also need to define the enum type, Honorific:
Code Block | ||||
---|---|---|---|---|
| ||||
package com.example.tutorial.data;
public enum Honorific
{
MR, MRS, MISS, DR
}
|
...
First, we'll update the Index.tml template, to create a link to the new page:
Code Block | ||||
---|---|---|---|---|
| ||||
<h1>Address Book</h1>
<ul>
<li><t:pagelink page="address/create">Create new address</t:pagelink></li>
</ul>
|
Now we need the address/Create page; lets start with an empty shell, just to test our navigation.
Code Block | |||||
---|---|---|---|---|---|
| |||||
<html t:type="layout" title="Create New Address"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<em>coming soon ...</em>
</html>
|
And the corresponding class:
Code Block | ||||
---|---|---|---|---|
| ||||
package com.example.tutorial.pages.address;
public class CreateAddress
{
}
|
So ... why is the class named "CreateAddress" and not simply "Create"? Actually, we could have named it "Create", and the application would still work, but the longer class name is equally valid. Tapestry noticed the redundancy in the class name (com.example.tutorial.pages.address
.CreateAddress) and just stripped out the redundant suffix
Wiki Markup |
---|
{footnote}Tapestry also checks for redundant prefixes. In addition, the long name, "address/CreateAddress" is also valid.{footnote} |
.
Eventually, your application will probably have more entities: perhaps you'll have a "user/Create" page and a "payment/Create" page and an "account/Create" page. You could have a bunch of different classes all named Create spread across a number of different packages. That's legal Java, but it isn't ideal. You may find yourself accidentally editing the Java code for creating an Account when your really want to be editing the code for creating a Payment.
...
Add the following to the CreateAddress template (replacing the "coming soon ..." message):
Code Block | ||||
---|---|---|---|---|
| ||||
<t:beaneditform object="address"/>
|
And match that up with a property in the CreateAddress class:
Code Block | ||||
---|---|---|---|---|
| ||||
@Property
private Address address;
|
...
The BeanEditForm must guess at the right order to present the fields; for public fields, they end up in alphabetical order
Wiki Markup |
---|
{footnote}For standard JavaBeans properties, the BeanEditForm default is in the order in which the getter methods are defined in the class (it uses line number information, if available).{footnote} |
.
A better order for these fields is the order in which they are defined in the Address class:
...
We can accomplish this by using the reorder
parameter of the BeanEditForm component, which is a comma separated list of property (or public field) names:
Code Block | |||||
---|---|---|---|---|---|
| |||||
<t:beaneditform object="address"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone" />
|
...
No Format | ||
---|---|---|
| ||
street1-label=Street 1
street2-label=Street 2
email-label=E-Mail
zip-label=Zip Code
phone-label=Phone Number |
...
We can also customize the options in the drop down list. All we have to do is add some more entries to the message catalog matching the enum names to the desired labels. Update CreateAddress.properties and add:
No Format |
---|
MR=Mr.
MRS=Mrs.
DR=Dr.
|
Notice that we don't have to include an option for MISS, because that is converted to "Miss" anyway. You might just want to include it for sake of consistency ... the point is, each option label is searched for separately.
...
That button is a component within the BeanEditForm component. It's not a property, so we can't just put a message into the message catalog, the way we can with the fields. Fortunately, the BeanEditForm component includes a parameter expressly for re-labeling the button. Simply change the CreateAddress component template:
Code Block | ||
---|---|---|
| ||
<t:beaneditform submitlabel="Create Address" object="address"/>
|
...
Here we want to reference a message from the catalog, so we use the "message:" prefix:
Code Block | ||
---|---|---|
| ||
<t:beaneditform object="address" submitlabel="message:submit-label"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone" />
|
And then define the submit-label key in the message catalog:
No Format |
---|
submit-label=Create Address
|
...
Edit the Address entity, and update the lastName, firstName, street1, city, state and zip fields, adding a @Validate annotation to each:
Code Block | ||
---|---|---|
| ||
@Validate("required")
public String firstName;
|
...
So ... how about some more interesting validation than just "required or not". Tapestry has built in support for validating based on field length and several variations of field value, including regular expressions. Zip codes are pretty easy to express as a regular expression.
Code Block | ||
---|---|---|
| ||
@Validate("required,regexp=^\\d{5}(-\\d{4})?$")
public String zip;
|
...
Fortunately, it's easy to customize validation messages. All we need to know is the name of the property ("zip") and the name of the validator ("regexp"). We can then put an entry into the CreateAddress message catalog:
No Format |
---|
zip-regexp-message=Zip Codes are five or nine digits. Example: 02134 or 90125-1655.
|
...
Let's go one step further. Turns out, we can move the regexp pattern to the message catalog as well. If you only provide the name of the validator in the @Validate annotation, Tapestry will search the containing page's message catalog of the constraint value, as well as the validation message. The constraint value for the regexp validator is the regular expression to match against.
Code Block | ||
---|---|---|
| ||
@Validate("required,regexp")
public String zip;
|
Now, just put the regular expression into the CreateAddress message catalog:
No Format |
---|
zip-regexp=^\\d{5}(-\\d{4})?$
zip-regexp-message=Zip Codes are five or nine digits. Example: 02134 or 90125-1655.
|
...
By now you are likely curious about what happens after the form submits successfully (without validation errors), so that's what we'll focus on next.
...
Wiki Markup |
---|
{display-footnotes} |
Wiki Markup |
---|
{scrollbar} |