...
We'll start with the entity data, a simple object to store the information we'll need. These classes go in an entities
sub-package. Unlike the use of the pages
sub-package (for page component classes), this is not enforced by Tapestry; it's just a convention (but as we'll see shortly, a handy one).
Code Block | ||
---|---|---|
|
...
Code Block | |
---|---|
package org.apache.tapestry5.tutorial.entities; import org.apache.tapestry5.tutorial.data.Honorific; public class Address { private Honorific honorific; private String firstName; private String lastName; private String street1; private String street2; private String city; private String state; private String zip; private String email; private String phone; public String getCity() { return city; } public String getEmail() { return email; } public String getFirstName() { return firstName; } public Honorific getHonorific() { return honorific; } public String getLastName() { return lastName; } public String getPhone() { return phone; } public String getState() { return state; } public String getStreet1() { return street1; } public String getStreet2() { return street2; } public String getZip() { return zip; } public void setCity(String city) { this.city = city; } public void setEmail(String email) { this.email = email; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setHonorific(Honorific honorific) { this.honorific = honorific; } public void setLastName(String lastName) { this.lastName = lastName; } public void setPhone(String phone) { this.phone = phone; } public void setState(String state) { this.state = state; } public void setStreet1(String street1) { this.street1 = street1; } public void setStreet2(String street2) { this.street2 = street2; } public void setZip(String zip) { this.zip = zip; } } |
It's just a collection of getter and setter methods. We also need to define the enum type, Honorific:
Code Block | ||
---|---|---|
|
...
Code Block | |
---|---|
package org.apache.tapestry5.tutorial.data; public enum Honorific { MR, MRS, MISS, DR } |
...
First, we'll update the Index.tml template, to create a link for creating a new page:
Code Block | ||||||
---|---|---|---|---|---|---|
|
...
Code Block | ||
---|---|---|
XML | XML | <h1>Address Book</h1> <ul> <li><t:pagelink page="address/create">Create new address</t:pagelink></li> </ul> |
Now we need the page, let's start with an empty shell, just to test our navigation.
Code Block | ||||||
---|---|---|---|---|---|---|
|
...
Code Block | ||
---|---|---|
XML | XML | <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"> <head> <title>Create New Address</title> </head> <body> <h1>Create New Address</h1> <em>coming soon ...</em> </body> </html> |
And the corresponding class:
Code Block | ||
---|---|---|
|
...
package org.apache.tapestry5.tutorial.pages.address; public class CreateAddress { } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
<t:beaneditform object="address"/>{noformat}
|
And
...
match
...
that
...
up
...
with
...
a
...
property
...
in
...
the
...
CreateAddress
...
class:
...
@Property
private Address address
Code Block |
---|
@Property
private Address address
|
When you refresh the page, you'll see the following:
Initial version of the create address form
There have been minor changes to the default CSS since this screenshot was taken; for example, the labels are a bit wider now. In addition, the Honorific field (being optional) would include a blank option, rather than the first real selection, "Mr".
Tapestry's done quite a bit of work here. It has created a form that includes a field for each property. Further, its seen that the honorific property is an enumerated type, and presented that as a drop-down list.
In addition, Tapestry has converted the property names ("city", "email", "firstName") to user presentable labels ("City", "Email", "First Name"). In fact, these are <label> elements, so clicking a label will move the cursor into the corresponding field.
This is an awesome start; it's a presentable interface, quite nice in fact for a few minute's work. But it's far from perfect; let's get started with some customizations.
Changing field order
It looks like the fields are being displayed in alphabetical order, ("city" first, "zip" last). That's not quite the reality, however: If you check the listing for the Address class, you'll see that the getter and setter methods are in alphabetical order (care of Eclipse, which generated all those methods from the fields).
The BeanEditForm works in the order in which the getter methods are defined in the class. Let's reorder them into a more reasonable order:
- honorific
- firstName
- lastName
- street1
- street2
- city
- state
- zip
- phone
(This is also the order of in which the fields are defined.)
Because Address is not a component class, it is necessary to restart Jetty to see the effects of these changes.
Once Jetty is restarted, hit the browser's refresh button to see the fields in the correct order:
Create address form with fields in proper order
Customizing labels
Tapestry makes it pretty easy to customize the labels used on the fields. It's just a matter of creating a message catalog for the page.
In Tapestry, every page and component may have its own message catalog. This is a standard Java properties file, and it is named the same as the page or component class, with a ".properties" extension. A message catalog consists of a series of lines, each line is a message key and a message value separated with an equals sign.
All it takes is to create a message entry with a particular name: the name of the property suffixed with "-label". As elsewhere, Tapestry is forgiving of case.
No Format | |||
---|---|---|---|
Code Block | |||
|
| ||
:* {noformat} street1-label=Street 1 street2-label=Street 2 email-label=E-Mail zip-label=Zip Code phone-label=Phone Number{noformat} |
Since
...
this
...
is
...
a
...
new
...
file
...
(and
...
not
...
a
...
change
...
to
...
an
...
existing
...
file),
...
you
...
may
...
have
...
to
...
restart
...
Jetty
...
to
...
force
...
Tapestry to pick up the change.
Create Address form with field labels corrected
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 |
---|
to pick up the change. !address-v3.png|border=1,width=760,height=446! Create Address form with field labels corrected 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: {noformat} MR=Mr. MRS=Mrs. DR=Dr.{noformat} |
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.
...
Lastly,
...
the
...
default
...
label
...
on
...
the
...
submit
...
button
...
is
...
"Create/Update"
...
(BeanEditForm
...
doesn't
...
know
...
how
...
it
...
is
...
being
...
used).
...
Let's
...
change
...
that
...
to
...
"Create
...
Address".
...
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 | ||||
---|---|---|---|---|
| ||||
{code:XML}
<t:beaneditform submitlabel="Create Address" object="address"/>
|
...