Author: buildbot
Date: Sat Feb 3 22:21:02 2018
New Revision: 1024796
Log:
Production update by buildbot for tapestry
Modified:
websites/production/tapestry/content/cache/main.pageCache
websites/production/tapestry/content/creating-the-skeleton-application.html
websites/production/tapestry/content/exploring-the-project.html
websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html
websites/production/tapestry/content/using-beaneditform-to-create-user-forms.html
websites/production/tapestry/content/using-tapestry-with-hibernate.html
Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.
Modified:
websites/production/tapestry/content/creating-the-skeleton-application.html
==============================================================================
--- websites/production/tapestry/content/creating-the-skeleton-application.html
(original)
+++ websites/production/tapestry/content/creating-the-skeleton-application.html
Sat Feb 3 22:21:02 2018
@@ -91,10 +91,10 @@
<localRepository>C:/Users/joeuser/.m2/repository</localRepository>
</settings>
</pre>
-</div></div><p>Of course, adjust the <code>localRepository</code> element to
match the correct path for your computer.</p><h3
id="CreatingTheSkeletonApplication-CreateProject">Create Project</h3><p>Okay,
let's get started creating our new project.</p><p>In Eclipse, go to
<strong>File > New ></strong> <strong>Project... > Maven > Maven
Project</strong></p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/select-a-wizard.png?version=1&modificationDate=1416675284000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/select-a-wizard.png?version=1&modificationDate=1416675284000&api=v2"></span></p><p>Then
click <strong>Next</strong>, <strong>Next</strong> (again), and then on the
<strong>Select an Archetype</strong> page click the <strong>Configure</strong>
button on the Catal
og line. The <strong>Archetype</strong> preferences dialog should appear.
Click the <strong>Add Remote Catalog...</strong> button, as shown
below:</p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/add-archetype-catalog.png?version=1&modificationDate=1416675354000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/add-archetype-catalog.png?version=1&modificationDate=1416675354000&api=v2"></span></p><p>As
shown above, enter <span class="nolink"><span class="nolink">"<span
class="nolink">http://tapestry.apache.org</span>"</span></span> in the Catalog
File field, and "Apache Tapestry" in the Description field.</p><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon">
</span><div class="confluence-information-macro-body"><p>If you want to try an
unreleased (alpha or beta) version of Tapestry, use <span class="nolink">the
<strong><a class="external-link"
href="https://repository.apache.org/content/repositories/staging">https://repository.apache.org/content/repositories/staging</a></strong></span>
archetype catalog file instead.</p></div></div><p>Click <strong>OK</strong>,
then<strong> OK</strong> again.</p><p>On the Select an Archetype dialog (shown
below), select the newly-added Apache Tapestry catalog, then select the
"quickstart" artifact from the list and click
<strong>Next</strong>.</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/select-archetype.png?version=1&modificationDate=1416675447000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/sel
ect-archetype.png?version=1&modificationDate=1416675447000&api=v2"></span></p><p><em><strong>Note:</strong>
Screenshots in this tutorial may show different (either newer or older)
versions of Tapestry than you may see.</em></p><p>Fill in the Group Id,
Artifact Id, Version and Package  as follows:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/specify-archetype-parameters.png?version=1&modificationDate=1416675494000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/specify-archetype-parameters.png?version=1&modificationDate=1416675494000&api=v2"></span></p><p>then
click Finish.</p><div class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div c
lass="confluence-information-macro-body"><p>The first time you use Maven,
project creation may take a while as Maven downloads a large number of JAR
dependencies for Maven, Jetty and Tapestry. These downloaded files are cached
locally and will not need to be downloaded again, but you do have to be patient
on first use.</p></div></div><p>After Maven finishes, you'll see a new
directory, <code>tutorial1, in your Package Explorer view in
Eclipse.</code></p><h2
id="CreatingTheSkeletonApplication-RunningtheApplicationusingJetty">Running the
Application using Jetty</h2><p>One of the first things you can do is use Maven
to run Jetty directly.</p><p>Right-click on the <code>tutorial1</code> project
in your Package Explorer view and select <strong>Run As > Maven Build...
></strong>, enter a Goal of <strong>"jetty:run"</strong>. This creates a
"Run Configuration" named "tutorial1" that we'll use throughout this tutorial
to start the app:</p><p><span class="confluence-embedded-file-wrapp
er"><img class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/run-configuration.png?version=2&modificationDate=1416744425000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/run-configuration.png?version=2&modificationDate=1416744425000&api=v2"></span></p><p>Tapestry
runs best with a couple of additional options; click the "JRE" tab and enter
the following VM
Arguments:</p><pre></pre><p>-XX:MaxPermSize=256M</p><p>-Xmx600m</p><p>-Dtapestry.execution-mode=development</p>
+</div></div><p>Of course, adjust the <code>localRepository</code> element to
match the correct path for your computer.</p><h3
id="CreatingTheSkeletonApplication-CreateProject">Create Project</h3><p>Okay,
let's get started creating our new project.</p><p>In Eclipse, go to
<strong>File > New ></strong> <strong>Project... > Maven > Maven
Project</strong></p><p><strong><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="613"
src="creating-the-skeleton-application.data/select-a-wizard.png"></span><br
clear="none"></strong></p><p>Then click <strong>Next</strong>,
<strong>Next</strong> (again), and then on the <strong>Select an
Archetype</strong> page click the <strong>Configure</strong> button on the
Catalog line. The <strong>Archetype</strong> preferences dialog should appear.
Click the <strong>Add Remote Catalog...</strong> button, as shown
below:</p><p><span class="confluence-embedded-file-wrapper conf
luence-embedded-manual-size"><img class="confluence-embedded-image"
width="900"
src="creating-the-skeleton-application.data/add-archetype-catalog.png"></span></p><p>As
shown above, enter <span class="nolink"><span class="nolink">"<span
class="nolink">http://tapestry.apache.org</span>"</span></span> in the Catalog
File field, and "Apache Tapestry" in the Description field.</p><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>If you want to try an unreleased
(alpha or beta) version of Tapestry, use <span class="nolink">the <strong><a
class="external-link"
href="https://repository.apache.org/content/repositories/staging">https://repository.apache.org/content/repositories/staging</a></strong></span>
archetype catalog file instead.</p></div></div><p>Click <strong>OK</strong>,
then<strong> OK</strong> again.</p
><p>On the Select an Archetype dialog (shown below), select the newly-added
>Apache Tapestry catalog, then select the "quickstart" artifact from the list
>and click <strong>Next</strong>.</p><p><span
>class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
>class="confluence-embedded-image" width="804"
>src="creating-the-skeleton-application.data/select-archetype.png"></span></p><p> </p><p><em><strong>Note:</strong>
> Screenshots in this tutorial may show different (either newer or older)
>versions of Tapestry than you may see.</em></p><p>Fill in the Group Id,
>Artifact Id, Version and Package  as follows:</p><p><span
>class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
>class="confluence-embedded-image" width="530"
>src="creating-the-skeleton-application.data/specify-archetype-parameters.png"></span></p><p>then
> click Finish.</p><div class="confluence-information-macro
>confluence-information-macro-information"><span class="aui-icon aui-i
con-small aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>The first time you use Maven,
project creation may take a while as Maven downloads a large number of JAR
dependencies for Maven, Jetty and Tapestry. These downloaded files are cached
locally and will not need to be downloaded again, but you do have to be patient
on first use.</p></div></div><p>After Maven finishes, you'll see a new
directory, <code>tutorial1, in your Package Explorer view in
Eclipse.</code></p><h2
id="CreatingTheSkeletonApplication-RunningtheApplicationusingJetty">Running the
Application using Jetty</h2><p>One of the first things you can do is use Maven
to run Jetty directly.</p><p>Right-click on the <code>tutorial1</code> project
in your Package Explorer view and select <strong>Run As > Maven Build...
></strong>, enter a Goal of <strong>"jetty:run"</strong>. This creates a
"Run Configuration" named "tutorial1" that we'll use throughout this
tutorial to start the app:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="568"
src="creating-the-skeleton-application.data/run-configuration.png"></span></p><p>Tapestry
runs best with a couple of additional options; click the "JRE" tab and enter
the following VM
Arguments:</p><pre></pre><p>-XX:MaxPermSize=256M</p><p>-Xmx600m</p><p>-Dtapestry.execution-mode=development</p>
-<p><code><em>(If you're using JDK 1.8 then you should omit the MaxPermSize
argument.)</em></code></p><p><code>Here's how it looks:<br
clear="none"></code></p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/run-configuration-jre.png?version=2&modificationDate=1416744425000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/run-configuration-jre.png?version=2&modificationDate=1416744425000&api=v2"></span></p><p>Finally,
click <strong>Run</strong>.</p><p>Again, the first time, there's a dizzying
number of downloads, but before you know it, the Jetty servlet container is up
and running.</p><p>Once Jetty is initialized (which only takes a few seconds
after the first time), you'll see the following in your console:</p><p><span
class="confluence-embedded-file-wrapper"><img class="conflue
nce-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/console-startup.png?version=2&modificationDate=1416797756000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/console-startup.png?version=2&modificationDate=1416797756000&api=v2"></span></p><p><em>Note
the red square icon above. Later on you'll use that icon to stop Jetty before
restarting the app.</em></p><p>You can now open a web browser to <a
class="external-link" href="http://localhost:8080/tutorial1/"
rel="nofollow">http://localhost:8080/tutorial1/</a> to see the running
application:</p><p> </p><p><span class="confluence-embedded-file-wrapper
image-left-wrapper"><img class="confluence-embedded-image
confluence-external-resource confluence-content-image-border image-left"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/startpage.png?version=7&modificationDat
e=1416798158000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340356/startpage.png?version=7&modificationDate=1416798158000&api=v2"></span></p><p> </p><div
style="clear: both"></div><p style="text-align: left;"><strong><em>NOTE: Your
screen may look very different depending on the version of Tapestry you are
using!</em></strong></p><p style="text-align: left;">The date and time in the
middle of the page shows that this is a live application.</p><p>This is a
complete little web app; it doesn't do much, but it demonstrate how to create a
number of pages sharing a common layout, and demonstrates some simple
navigation and link handling. You can see that it has several different pages
that share a common layout. (<span><em>Layout</em> is a loose term meaning
common look and feel and navigation across many or all of the pages of an
application. Often an application will include a Layout component to provide
that commonness.)</spa
n></p><p><span>Next: <a
href="creating-the-skeleton-application.html">Creating The Skeleton
Application</a></span></p><p><span><br clear="none"></span></p></div>
+<p><code><em>(If you're using JDK 1.8 then you should omit the MaxPermSize
argument.)</em></code></p><p><code>Here's how it
looks:</code></p><p><code><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="666"
src="creating-the-skeleton-application.data/run-configuration-jre.png"></span><br
clear="none"></code></p><p>Finally, click <strong>Run</strong>.</p><p>Again,
the first time, there's a dizzying number of downloads, but before you know it,
the Jetty servlet container is up and running.</p><p>Once Jetty is initialized
(which only takes a few seconds after the first time), you'll see the following
in your console:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="865"
src="creating-the-skeleton-application.data/console-startup.png"></span></p><p><em>Note
the red square icon above. Later on you'll use that icon to stop Jetty before
restarting the app.</em></p><p>You can now open a web browser to <a
class="external-link" href="http://localhost:8080/tutorial1/"
rel="nofollow">http://localhost:8080/tutorial1/</a> to see the running
application:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="785"
src="creating-the-skeleton-application.data/startpage.png"></span></p><p> </p><div
style="clear: both"></div><p style="text-align: left;"><strong><em>NOTE: Your
screen may look very different depending on the version of Tapestry you are
using!</em></strong></p><p style="text-align: left;">The date and time in the
middle of the page shows that this is a live application.</p><p>This is a
complete little web app; it doesn't do much, but it demonstrate how to create a
number of pages sharing a common layout, and demonstrates some simple
navigation and link handling. You can see that it has several different pages
that share a common layo
ut. (<span><em>Layout</em> is a loose term meaning common look and feel and
navigation across many or all of the pages of an application. Often an
application will include a Layout component to provide that
commonness.)</span></p><p><span>Next: <a
href="creating-the-skeleton-application.html">Creating The Skeleton
Application</a></span></p><p><span><br clear="none"></span></p></div>
</div>
<div class="clearer"></div>
Modified: websites/production/tapestry/content/exploring-the-project.html
==============================================================================
--- websites/production/tapestry/content/exploring-the-project.html (original)
+++ websites/production/tapestry/content/exploring-the-project.html Sat Feb 3
22:21:02 2018
@@ -262,13 +262,13 @@ public class Index
-<span class="gliffy-container" id="gliffy-container-24346949-9257"
data-fullwidth="913" data-ceoid="24188263"
data-edit="${diagramEditLink.getLinkUrl()}"
data-full="${diagramZoomLink.getLinkUrl()}" data-filename="Templates and
Parameters">
+<span class="gliffy-container" id="gliffy-container-24346949-8526"
data-fullwidth="913" data-ceoid="24188263"
data-edit="${diagramEditLink.getLinkUrl()}"
data-full="${diagramZoomLink.getLinkUrl()}" data-filename="Templates and
Parameters">
- <map id="gliffy-map-24346949-7684" name="gliffy-map-24346949-7684"></map>
+ <map id="gliffy-map-24346949-7884" name="gliffy-map-24346949-7884"></map>
- <img class="gliffy-image gliffy-image-border"
id="gliffy-image-24346949-9257" width="304" height="300" data-full-width="913"
data-full-height="901"
src="https://cwiki.apache.org/confluence/download/attachments/24188263/Templates%20and%20Parameters.png?version=2&modificationDate=1371888025000&api=v2"
alt="Templates and Parameters" usemap="#gliffy-map-24346949-7684">
+ <img class="gliffy-image gliffy-image-border"
id="gliffy-image-24346949-8526" width="304" height="300" data-full-width="913"
data-full-height="901"
src="https://cwiki.apache.org/confluence/download/attachments/24188263/Templates%20and%20Parameters.png?version=2&modificationDate=1371888025000&api=v2"
alt="Templates and Parameters" usemap="#gliffy-map-24346949-7884">
- <map class="gliffy-dynamic" id="gliffy-dynamic-map-24346949-9257"
name="gliffy-dynamic-map-24346949-9257"></map>
+ <map class="gliffy-dynamic" id="gliffy-dynamic-map-24346949-8526"
name="gliffy-dynamic-map-24346949-8526"></map>
</span>
@@ -281,9 +281,9 @@ public class Index
return "A great day to learn Tapestry";
}
</pre>
-</div></div><p>Make sure you save changes; then click the refresh link in the
web browser:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-external-resource" width="700"
src="https://cwiki-test.apache.org/confluence/download/attachments/24188263/app-live-reload.png?version=5&modificationDate=1416707986000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/24188263/app-live-reload.png?version=5&modificationDate=1416707986000&api=v2"></span></p><div
class="navmenu" style="float:right; width:30%; background:white; margin:3px;
padding:3px">
+</div></div><p>Make sure you save changes; then click the refresh link in the
web browser:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="700"
src="exploring-the-project.data/app-live-reload.png"></span></p><p> </p><div
class="navmenu" style="float:right; width:30%; background:white; margin:3px;
padding:3px">
<div class="confluence-information-macro
confluence-information-macro-note"><span class="aui-icon aui-icon-small
aui-iconfont-warning confluence-information-macro-icon"></span><div
class="confluence-information-macro-body">
-<p>If Live Class Reloading isn't working for you, check the Troubleshooting
section at <a href="class-reloading.html" title="Class Reloading">Class
Reloading</a>.</p></div></div></div>This is one of Tapestry's early <em>wow
factor</em> features: changes to your component classes are picked up
immediately (a feature we call Live Class Reloading). No restart. No re-deploy.
Make the changes and see them <em>now</em>. Nothing should slow you down or get
in the way of you getting your job done.<p>But ... what if you make a mistake?
What if you got the name in the template wrong. Give it a try; in the template,
change ${currentTime} to, say, ${currenTime}, and see what you get:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image confluence-external-resource" width="700"
src="https://cwiki-test.apache.org/confluence/download/attachments/24188263/app-error-1.png?version=3&modificationDate=1416707595000&api=v2"
d
ata-image-src="https://cwiki-test.apache.org/confluence/download/attachments/24188263/app-error-1.png?version=3&modificationDate=1416707595000&api=v2"></span></p><p>This
is Tapestry's exception report page. It's quite detailed. It clearly
identifies what Tapestry was doing, and relates the problem to a specific line
in the template, which is shown in context. Tapestry always expands out the
entire stack of exceptions, because it is so common for exceptions to be
thrown, caught, and re-thrown inside other exceptions. In fact, if we scroll
down just a little bit, we see more detail about this exception, plus a little
bit of help:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-external-resource" width="700"
src="https://cwiki-test.apache.org/confluence/download/attachments/24188263/app-error-2.png?version=4&modificationDate=1416707595000&api=v2"
data-image-src="https://cwiki-test.apac
he.org/confluence/download/attachments/24188263/app-error-2.png?version=4&modificationDate=1416707595000&api=v2"></span></p><p>This
is part of Tapestry's way: it not only spells out exactly what it was doing
and what went wrong, but it even helps you find a solution; here it tells you
the names of properties you could have used.</p><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>This level of detail reflects that
the application has been configured to run in <em>development mode</em> instead
of <em>production mode</em>. In production mode, the exception report would
simply be the top level exception message. However, most production
applications go further and customize how Tapestry handles and reports
exceptions.</p></div></div><p>Tapestry displays the stack trace of the deepest
exception, along
with lots of details about the run-time environment: details about the
current request, the HttpSession (if one exists), and even a detailed list of
all JVM system properties. Scroll down to see all this information.</p><p>Next:
<a href="exploring-the-project.html">Exploring the
Project</a></p><p> </p><p></p></div>
+<p>If Live Class Reloading isn't working for you, check the Troubleshooting
section at <a href="class-reloading.html" title="Class Reloading">Class
Reloading</a>.</p></div></div></div>This is one of Tapestry's early <em>wow
factor</em> features: changes to your component classes are picked up
immediately (a feature we call Live Class Reloading). No restart. No re-deploy.
Make the changes and see them <em>now</em>. Nothing should slow you down or get
in the way of you getting your job done.<p>But ... what if you make a mistake?
What if you got the name in the template wrong. Give it a try; in the template,
change ${currentTime} to, say, ${currenTime}, and see what you get:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="700"
src="exploring-the-project.data/app-error-1.png"></span></p><p>This is
Tapestry's exception report page. It's quite detailed. It clearly identifies
what Tapestry was doing, and re
lates the problem to a specific line in the template, which is shown in
context. Tapestry always expands out the entire stack of exceptions, because it
is so common for exceptions to be thrown, caught, and re-thrown inside other
exceptions. In fact, if we scroll down just a little bit, we see more detail
about this exception, plus a little bit of help:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="700"
src="exploring-the-project.data/app-error-2.png"></span></p><p> </p><p>This
is part of Tapestry's way: it not only spells out exactly what it was doing
and what went wrong, but it even helps you find a solution; here it tells you
the names of properties you could have used.</p><div
class="confluence-information-macro
confluence-information-macro-information"><span class="aui-icon aui-icon-small
aui-iconfont-info confluence-information-macro-icon"></span><div
class="confluence-information-macro-bo
dy"><p>This level of detail reflects that the application has been configured
to run in <em>development mode</em> instead of <em>production mode</em>. In
production mode, the exception report would simply be the top level exception
message. However, most production applications go further and customize how
Tapestry handles and reports exceptions.</p></div></div><p>Tapestry displays
the stack trace of the deepest exception, along with lots of details about the
run-time environment: details about the current request, the HttpSession (if
one exists), and even a detailed list of all JVM system properties. Scroll down
to see all this information.</p><p>Next: <a
href="implementing-the-hi-lo-guessing-game.html">Implementing the Hi-Lo
Guessing Game</a></p><p> </p><p></p></div>
</div>
<div class="clearer"></div>
Modified:
websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html
==============================================================================
---
websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html
(original)
+++
websites/production/tapestry/content/implementing-the-hi-lo-guessing-game.html
Sat Feb 3 22:21:02 2018
@@ -76,7 +76,7 @@
</div>
<div id="content">
- <div id="ConfluenceContent"><p><span><br
clear="none"></span></p><p><span>Let's start building a basic Hi-Lo Guessing
game.</span></p><p>In the game, the computer selects a number between 1 and 10.
You try and guess the number, clicking links. At the end, the computer tells
you how many guesses you required to identify the target number. Even a simple
example like this will demonstrate several important concepts in
Tapestry:</p><ul><li>Breaking an application into individual
pages</li><li>Transferring information from one page to
another</li><li>Responding to user interactions</li><li>Storing client
information in the server-side session</li></ul><p>We'll build this little
application in small pieces, using the kind of iterative development that
Tapestry makes so easy.</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/hilo
-flow.png?version=2&modificationDate=1286814202000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/hilo-flow.png?version=2&modificationDate=1286814202000&api=v2"></span></p><p>Our
page flow is very simple, consisting of three pages: Index (the starting
page), Guess and GameOver. The Index page introduces the application and
includes a link to start guessing. The Guess page presents the user with ten
links, plus feedback such as "too low" or "too high". The GameOver page tells
the user how many guesses they took before finding the target number.</p><h1
id="ImplementingtheHi-LoGuessingGame-IndexPage">Index Page</h1><p>Let's get to
work on the Index page and template. Make Index.tml look like this:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.tml</b></div><div
class="codeContent panelContent pdl">
+ <div id="ConfluenceContent"><p><span><br
clear="none"></span></p><p><span>Let's start building a basic Hi-Lo Guessing
game.</span></p><p>In the game, the computer selects a number between 1 and 10.
You try and guess the number, clicking links. At the end, the computer tells
you how many guesses you required to identify the target number. Even a simple
example like this will demonstrate several important concepts in
Tapestry:</p><ul><li>Breaking an application into individual
pages</li><li>Transferring information from one page to
another</li><li>Responding to user interactions</li><li>Storing client
information in the server-side session</li></ul><p>We'll build this little
application in small pieces, using the kind of iterative development that
Tapestry makes so easy.</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image"
src="implementing-the-hi-lo-guessing-game.data/hilo-flow.png"></span></p><p>Our
page flow is very simple, con
sisting of three pages: Index (the starting page), Guess and GameOver. The
Index page introduces the application and includes a link to start guessing.
The Guess page presents the user with ten links, plus feedback such as "too
low" or "too high". The GameOver page tells the user how many guesses they took
before finding the target number.</p><h1
id="ImplementingtheHi-LoGuessingGame-IndexPage">Index Page</h1><p>Let's get to
work on the Index page and template. Make Index.tml look like this:</p><div
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader
panelHeader pdl" style="border-bottom-width: 1px;"><b>Index.tml</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"><html t:type="layout" title="Hi/Lo Guess"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
@@ -96,12 +96,12 @@ public class Index
{
}
</pre>
-</div></div><p>Running the application gives us our start:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/hilo-1.png?version=3&modificationDate=1416879474000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/hilo-1.png?version=3&modificationDate=1416879474000&api=v2"></span></p><p>However,
clicking the link doesn't do anything yet, as its just a placeholder <a>
tag, not an actual Tapestry component. Let's think about what should happen
when the user clicks that link:</p><ul><li>A random target number between 1 and
10 should be selected</li><li>The number of guesses taken should be reset to
0</li><li>The user should be sent to the Guess page to make a
guess</li></ul><p>Our first step is to find out when the user clicks that
"start guessing" link. In a typical web applica
tion framework, we might start thinking about URLs and handlers and maybe some
sort of XML configuration file. But this is Tapestry, so we're going to work
with components and methods on our classes.</p><p>First, the component. We want
to perform an action (selecting the number) before continuing on to the Guess
page. The ActionLink component is just what we need; it creates a link with a
URL that will trigger an action event in our code ... but that's getting ahead
of ourselves. First up, convert the <a> tag to an ActionLink
component:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Index.tml (partial)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>Running the application gives us our start:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image"
src="implementing-the-hi-lo-guessing-game.data/hilo-1.png"></span></p><p>However,
clicking the link doesn't do anything yet, as its just a placeholder <a>
tag, not an actual Tapestry component. Let's think about what should happen
when the user clicks that link:</p><ul><li>A random target number between 1 and
10 should be selected</li><li>The number of guesses taken should be reset to
0</li><li>The user should be sent to the Guess page to make a
guess</li></ul><p>Our first step is to find out when the user clicks that
"start guessing" link. In a typical web application framework, we might start
thinking about URLs and handlers and maybe some sort of XML configuration file.
But this is Tapestry, so we're going to work with components and methods on our
classes.</p><p>First, the component. We want to perform an action (selecting t
he number) before continuing on to the Guess page. The ActionLink component is
just what we need; it creates a link with a URL that will trigger an action
event in our code ... but that's getting ahead of ourselves. First up, convert
the <a> tag to an ActionLink component:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>Index.tml (partial)</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <p>
<t:actionlink t:id="start">start guessing</t:actionlink>
</p>
</pre>
-</div></div><p>If you refresh the browser and hover your mouse over the "start
guessing" link, you'll see that its URL is now /tutorial1/index.start, which
identifies the name of the page ("index") and the id of the component
("start").</p><p>If you click the link now, you'll get an error:</p><p><span
class="confluence-embedded-file-wrapper image-center-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image
confluence-external-resource image-center" width="500"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/Application_Exception.png?version=1&modificationDate=1428077959000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/Application_Exception.png?version=1&modificationDate=1428077959000&api=v2"></span></p><p> </p><p>Tapestry
is telling us that we need to provide some kind of event handler for that
event. What does that look like?</p><p>An event handler is a method
of the Java class with a special name. The name is
<code>on</code><strong><em>Eventname</em></strong><code>From</code><strong><em>Component-id</em></strong>
... here we want a method named <code>onActionFromStart()</code>. How do we
know that "action" is the right event name? Because that's what ActionLink
does, that's why its named <strong><em>Action</em></strong>Link.</p><p>Once
again, Tapestry gives us options; if you don't like naming conventions, there's
an @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/OnEvent.html">OnEvent</a>
annotation you can place on the method instead, which restores the freedom to
name the method as you like. Details about this approach are in the <a
href="implementing-the-hi-lo-guessing-game.html">Tapestry Users' Guide</a>.
We'll be sticking with the naming convention approach for the
tutorial.</p><p>When handling a component event request (the kind of request
triggered by the ActionLink co
mponent's URL), Tapestry will find the component and trigger a component event
on it. This is the callback our server-side code needs to figure out what the
user is doing on the client side. Let's start with an empty event
handler:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Index.java</b></div><div class="codeContent panelContent pdl">
+</div></div><p>If you refresh the browser and hover your mouse over the "start
guessing" link, you'll see that its URL is now /tutorial1/index.start, which
identifies the name of the page ("index") and the id of the component
("start").</p><p>If you click the link now, you'll get an error:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="500"
src="implementing-the-hi-lo-guessing-game.data/hilo-index-missing-action-error.png"></span></p><p>Tapestry
is telling us that we need to provide some kind of event handler for that
event. What does that look like?</p><p>An event handler is a method of the Java
class with a special name. The name is
<code>on</code><strong><em>Eventname</em></strong><code>From</code><strong><em>Component-id</em></strong>
... here we want a method named <code>onActionFromStart()</code>. How do we
know that "action" is the right event name? Because that's what ActionLink
does, that's
why its named <strong><em>Action</em></strong>Link.</p><p>Once again, Tapestry
gives us options; if you don't like naming conventions, there's an @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/OnEvent.html">OnEvent</a>
annotation you can place on the method instead, which restores the freedom to
name the method as you like. Details about this approach are in the <a
href="implementing-the-hi-lo-guessing-game.html">Tapestry Users' Guide</a>.
We'll be sticking with the naming convention approach for the
tutorial.</p><p>When handling a component event request (the kind of request
triggered by the ActionLink component's URL), Tapestry will find the component
and trigger a component event on it. This is the callback our server-side code
needs to figure out what the user is doing on the client side. Let's start with
an empty event handler:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeHeader panelHea
der pdl" style="border-bottom-width: 1px;"><b>Index.java</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;">package com.example.tutorial1.pages;
public class Index
@@ -167,7 +167,7 @@ public class Index
}
}
</pre>
-</div></div><p>The new event handler method now chooses the target number, and
tells the Guess page about it. Because Tapestry is a managed environment, we
don't just create an instance of Guess ... it is Tapestry's responsibility to
manage the life cycle of the Guess page. Instead, we ask Tapestry for the Guess
page, using the @InjectPage annotation.</p><div
class="confluence-information-macro confluence-information-macro-note"><span
class="aui-icon aui-icon-small aui-iconfont-warning
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>All fields in a Tapestry page or
component class must be <strong>non-public</strong>.</p></div></div><p>Once we
have that Guess page instance, we can invoke methods on it
normally.</p><p>Returning a page instance from an event handler method directs
Tapestry to send a client-side redirect to the returned page, rather than
sending a redirect for the active page. Thus once the user clicks the "start
guessing" lin
k, they'll see the Guess page.</p><div class="confluence-information-macro
confluence-information-macro-warning"><span class="aui-icon aui-icon-small
aui-iconfont-error confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>When creating your own
applications, make sure that the objects stored in final variables are thread
safe. It seems counter-intuitive, but final variables are shared across many
threads. Ordinary instance variables are not. Fortunately, the implementation
of Random is, in fact, thread safe.</p></div></div><p>So ... let's click the
link and see what we get:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-template-missing.png?version=2&modificationDate=1416710821000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-tem
plate-missing.png?version=2&modificationDate=1416710821000&api=v2"></span></p><p>Ah!
We didn't create a Guess page template. Tapestry was really expecting us to
create one, so we better do so.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width:
1px;"><b>src/main/resources/com/example/tutorial/pages/Guess.tml</b></div><div
class="codeContent panelContent pdl">
+</div></div><p>The new event handler method now chooses the target number, and
tells the Guess page about it. Because Tapestry is a managed environment, we
don't just create an instance of Guess ... it is Tapestry's responsibility to
manage the life cycle of the Guess page. Instead, we ask Tapestry for the Guess
page, using the @InjectPage annotation.</p><div
class="confluence-information-macro confluence-information-macro-note"><span
class="aui-icon aui-icon-small aui-iconfont-warning
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>All fields in a Tapestry page or
component class must be <strong>non-public</strong>.</p></div></div><p>Once we
have that Guess page instance, we can invoke methods on it
normally.</p><p>Returning a page instance from an event handler method directs
Tapestry to send a client-side redirect to the returned page, rather than
sending a redirect for the active page. Thus once the user clicks the "start
guessing" lin
k, they'll see the Guess page.</p><div class="confluence-information-macro
confluence-information-macro-warning"><span class="aui-icon aui-icon-small
aui-iconfont-error confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>When creating your own
applications, make sure that the objects stored in final variables are thread
safe. It seems counter-intuitive, but final variables are shared across many
threads. Ordinary instance variables are not. Fortunately, the implementation
of Random is, in fact, thread safe.</p></div></div><p>So ... let's click the
link and see what we get:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="900"
src="implementing-the-hi-lo-guessing-game.data/guess-template-missing.png"></span></p><p>Ah!
We didn't create a Guess page template. Tapestry was really expecting us to
create one, so we better do so.</p><div class="code panel pdl" style="bo
rder-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width:
1px;"><b>src/main/resources/com/example/tutorial/pages/Guess.tml</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"><html t:type="layout" title="Guess The Number"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
@@ -177,16 +177,16 @@ public class Index
</html>
</pre>
-</div></div><p>Hit the browser's back button, then click the "start guessing"
link again. We're getting closer:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-no-target-prop.png?version=2&modificationDate=1416711075000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-no-target-prop.png?version=2&modificationDate=1416711075000&api=v2"></span></p><p>If
you scroll down, you'll see the line of the Guess.tml template that has the
error. We have a field named target, but it is private and there's no
corresponding property, so Tapestry was unable to access it.</p><p>We just need
to write the missing JavaBeans accessor methods <code>getTarget()</code> (and
<code>setTarget()</code> for good measure). Or we could let Tapestry write
those methods instead:</p><div class="cod
e panel pdl" style="border-width: 1px;"><div class="codeContent panelContent
pdl">
+</div></div><p>Hit the browser's back button, then click the "start guessing"
link again. We're getting closer:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="900"
src="implementing-the-hi-lo-guessing-game.data/guess-no-target-prop.png"></span></p><p>If
you scroll down, you'll see the line of the Guess.tml template that has the
error. We have a field named target, but it is private and there's no
corresponding property, so Tapestry was unable to access it.</p><p>We just need
to write the missing JavaBeans accessor methods <code>getTarget()</code> (and
<code>setTarget()</code> for good measure). Or we could let Tapestry write
those methods instead:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Property
private int target;
</pre>
-</div></div><p>The @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Property.html">Property</a>
annotation very simply directs Tapestry to write the getter and setter method
for you. You only need to do this if you are going to reference the field from
the template.</p><p>We are getting very close but there's one last big oddity
to handle. Once you refresh the page you'll see that target is 0!</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-target-zero.png?version=3&modificationDate=1416879255000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-target-zero.png?version=3&modificationDate=1416879255000&api=v2"></span></p><p>What
gives? We know it was set to at least 1 ... where did the value go?</p><p>As no
ted above, Tapestry sends a redirect to the client after handling the event
request. That means that the rendering of the page happens in an entirely new
request. Meanwhile, at the end of each request, Tapestry wipes out the value in
each instance variable. So that means that target <em>was</em> a non-zero
number during the component event request ... but by the time the new page
render request comes up from the web browser to render the Guess page, the
value of the target field has reverted back to its default, zero.</p><p>The
solution here is to mark which fields have values that should persist from one
request to the next (and next, and next ...). That's what the @<a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a>
annotation is for:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
+</div></div><p>The @<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Property.html">Property</a>
annotation very simply directs Tapestry to write the getter and setter method
for you. You only need to do this if you are going to reference the field from
the template.</p><p>We are getting very close but there's one last big oddity
to handle. Once you refresh the page you'll see that target is 0!</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image"
src="implementing-the-hi-lo-guessing-game.data/guess-target-zero.png"></span></p><p>What
gives? We know it was set to at least 1 ... where did the value go?</p><p>As
noted above, Tapestry sends a redirect to the client after handling the event
request. That means that the rendering of the page happens in an entirely new
request. Meanwhile, at the end of each request, Tapestry wipes out the value in
each instance variable. So that means that tar
get <em>was</em> a non-zero number during the component event request ... but
by the time the new page render request comes up from the web browser to render
the Guess page, the value of the target field has reverted back to its default,
zero.</p><p>The solution here is to mark which fields have values that should
persist from one request to the next (and next, and next ...). That's what the
@<a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Persist.html">Persist</a>
annotation is for:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Property
@Persist
private int target;
</pre>
-</div></div><p>This doesn't have anything to do with database persistence
(that's coming up in a later chapter). It means that the value is stored in the
HttpSession between requests.</p><p>Go back to the Index page and click the
link again. Finally, we have a target number:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-target.png?version=3&modificationDate=1416879254000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-target.png?version=3&modificationDate=1416879254000&api=v2"></span></p><p>That's
enough for us to get started. Let's build out the Guess page, and get ready to
let the user make guesses. We'll show the count of guesses, and increment that
count when they make them. We'll worry about high and low and actually
selecting the correct value later.</p><
p>When building Tapestry pages, you sometimes start with the Java code and
build the template to match, and sometime start with the template and build the
Java code to match. Both approaches are valid. Here, lets start with the markup
in the template, then figure out what we need in the Java code to make it
work.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Guess.tml (revised)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>This doesn't have anything to do with database persistence
(that's coming up in a later chapter). It means that the value is stored in the
HttpSession between requests.</p><p>Go back to the Index page and click the
link again. Finally, we have a target number:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image"
src="implementing-the-hi-lo-guessing-game.data/guess-target.png"></span></p><p>That's
enough for us to get started. Let's build out the Guess page, and get ready to
let the user make guesses. We'll show the count of guesses, and increment that
count when they make them. We'll worry about high and low and actually
selecting the correct value later.</p><p>When building Tapestry pages, you
sometimes start with the Java code and build the template to match, and
sometime start with the template and build the Java code to match. Both
approaches are valid. Here, lets start with the markup in the template, then
figure out what we
need in the Java code to make it work.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeHeader panelHeader pdl"
style="border-bottom-width: 1px;"><b>Guess.tml (revised)</b></div><div
class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"><html t:type="layout" title="Guess The Number"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
@@ -238,7 +238,7 @@ public class Guess
}
</pre>
-</div></div><p>The revised version of Guess includes two new properties:
<code>current</code> and <code>guessCount</code>. There's also a handler for
the action event from the makeGuess ActionLink component; currently it just
increments the count.</p><p>Notice that the
<code>onActionFromMakeGuess()</code> method now has a parameter: the context
value that was encoded into the URL by the ActionLink. When then user clicks
the link, Tapestry will automatically extract the string from the URL, convert
it to an int and pass that int value into the event handler method. More
boilerplate code you don't have to write.</p><p>At this point, the page is
partially operational:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/guess-1.png?version=4&modificationDate=1416879255000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/
download/attachments/23340505/guess-1.png?version=4&modificationDate=1416879255000&api=v2"></span></p><p>Our
next step is to actually check the value provided by the user against the
target and provide feedback: either they guessed too high, or too low, or just
right. If they get it just right, we'll switch to the GameOver page with a
message such as "You guessed the number 5 in 2 guesses".</p><p>Let's start with
the Guess page; it now needs a new property to store the message to be
displayed to the user, and needs a field for the injected GameOver
page:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Guess.java (partial)</b></div><div class="codeContent panelContent
pdl">
+</div></div><p>The revised version of Guess includes two new properties:
<code>current</code> and <code>guessCount</code>. There's also a handler for
the action event from the makeGuess ActionLink component; currently it just
increments the count.</p><p>Notice that the
<code>onActionFromMakeGuess()</code> method now has a parameter: the context
value that was encoded into the URL by the ActionLink. When then user clicks
the link, Tapestry will automatically extract the string from the URL, convert
it to an int and pass that int value into the event handler method. More
boilerplate code you don't have to write.</p><p>At this point, the page is
partially operational:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image"
src="implementing-the-hi-lo-guessing-game.data/guess-1.png"></span></p><p>Our
next step is to actually check the value provided by the user against the
target and provide feedback: either they guessed too high, or too low, or just
right. If they get it just right, we'll switch to the GameOver page with a
message such as "You guessed the number 5 in 2 guesses".</p><p>Let's start with
the Guess page; it now needs a new property to store the message to be
displayed to the user, and needs a field for the injected GameOver
page:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>Guess.java (partial)</b></div><div class="codeContent panelContent
pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Property
@Persist(PersistenceConstants.FLASH)
private String message;
@@ -306,7 +306,7 @@ public class GameOver
</html>
</pre>
-</div></div><p>The result, when you guess correctly, should be
this:</p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/gameover.png?version=4&modificationDate=1416879255000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340505/gameover.png?version=4&modificationDate=1416879255000&api=v2"></span></p><p>That
wraps up the basics of Tapestry; we've demonstrated the basics of linking
pages together and passing information from page to page in code as well as
incorporating data inside URLs.</p><p>There's still more room to refactor this
toy application; for example, making it possible to start a new game from the
GameOver page (and doing it in a way that doesn't duplicate code). In addition,
later we'll see other ways of sharing information between pages that are less
cumbersome than the
setup-and-persist approach shown here.</p><p>Next up: let's find out how
Tapestry handles HTML forms and user input.</p><p>Next: <a
href="implementing-the-hi-lo-guessing-game.html">Implementing the Hi-Lo
Guessing Game</a></p><p></p><p> </p><p> </p></div>
+</div></div><p>The result, when you guess correctly, should be
this:</p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image"
src="implementing-the-hi-lo-guessing-game.data/gameover.png"></span></p><p>That
wraps up the basics of Tapestry; we've demonstrated the basics of linking pages
together and passing information from page to page in code as well as
incorporating data inside URLs.</p><p>There's still more room to refactor this
toy application; for example, making it possible to start a new game from the
GameOver page (and doing it in a way that doesn't duplicate code). In addition,
later we'll see other ways of sharing information between pages that are less
cumbersome than the setup-and-persist approach shown here.</p><p>Next up: let's
find out how Tapestry handles HTML forms and user input.</p><p>Next: <a
href="using-beaneditform-to-create-user-forms.html">Using BeanEditForm To
Create User Forms</a></p><p></p><p> </p><p> </p></div>
</div>
<div class="clearer"></div>
Modified:
websites/production/tapestry/content/using-beaneditform-to-create-user-forms.html
==============================================================================
---
websites/production/tapestry/content/using-beaneditform-to-create-user-forms.html
(original)
+++
websites/production/tapestry/content/using-beaneditform-to-create-user-forms.html
Sat Feb 3 22:21:02 2018
@@ -152,17 +152,17 @@ address/CreateAddress: com.example.tutor
</div></div><p>When you refresh the page, you may see a warning like the
following at the top of the page:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/hmac-warning.png?version=2&modificationDate=1416883285000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/hmac-warning.png?version=2&modificationDate=1416883285000&api=v2"></span></p><p>If
you see that, it means you need to invent an HMAC passphrase for your app.
Just edit your AppModule.java class (in your services package), adding a couple
of lines to the contributeApplicationDefaults method like the
following:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> // Set the HMAC pass phrase to secure object
data serialized to client
configuration.add(SymbolConstants.HMAC_PASSPHRASE, "");</pre>
-</div></div><p>but, instead of an empty string, insert a long, <strong>random
string of characters</strong> (like a very long and complex password, at least
30 characters) that you keep private.</p><p>After you do that, stop the app and
restart it, and click on the Create new address link again, and you'll see
something like this:</p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-initial.png?version=2&modificationDate=1416884366000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-initial.png?version=2&modificationDate=1416884366000&api=v2"></span></p><p>Tapestry
has done quite a bit of work here. It has created a form that includes a field
for each property. Further, it has seen that the honorific property is an
enumerated type, and presented that
as a drop-down list.</p><p>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 with the mouse will move the input cursor into the corresponding
field.</p><p>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.</p><h1
id="UsingBeanEditFormToCreateUserForms-ChangingFieldOrder">Changing Field
Order</h1><p>The BeanEditForm must guess at the right order to present the
fields; for public fields, they end up in alphabetical order. 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).</p><p>A better order for these fields is the order in which they
are defined in the Address class:</p><ul><li>honori
fic</li><li>firstName</li><li>lastName</li><li>street1</li><li>street2</li><li>city</li><li>state</li><li>zip</li><li>email</li><li>phone</li></ul><p>We
can accomplish this by using the <code>reorder</code> parameter of the
BeanEditForm component, which is a comma separated list of property (or public
field) names:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>CreateAddress.tml (partial)</b></div><div class="codeContent
panelContent pdl">
+</div></div><p>but, instead of an empty string, insert a long, <strong>random
string of characters</strong> (like a very long and complex password, at least
30 characters) that you keep private.</p><p>After you do that, stop the app and
restart it, and click on the Create new address link again, and you'll see
something like this:</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="485"
src="using-beaneditform-to-create-user-forms.data/create-address-initial.png"></span></p><p>Tapestry
has done quite a bit of work here. It has created a form that includes a field
for each property. Further, it has seen that the honorific property is an
enumerated type, and presented that as a drop-down list.</p><p>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 wi
th the mouse will move the input cursor into the corresponding
field.</p><p>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.</p><h1
id="UsingBeanEditFormToCreateUserForms-ChangingFieldOrder">Changing Field
Order</h1><p>The BeanEditForm must guess at the right order to present the
fields; for public fields, they end up in alphabetical order. 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).</p><p>A better order for these fields is the order in which they
are defined in the Address
class:</p><ul><li>honorific</li><li>firstName</li><li>lastName</li><li>street1</li><li>street2</li><li>city</li><li>state</li><li>zip</li><li>email</li><li>phone</li></ul><p>We
can accomplish this by using the <code>reorder</code> parameter of the
BeanEditForm c
omponent, which is a comma separated list of property (or public field)
names:</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeHeader panelHeader pdl" style="border-bottom-width:
1px;"><b>CreateAddress.tml (partial)</b></div><div class="codeContent
panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:beaneditform object="address"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
/>
</pre>
-</div></div><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-reordered.png?version=2&modificationDate=1416884592000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-reordered.png?version=2&modificationDate=1416884592000&api=v2"></span></p><h3
id="UsingBeanEditFormToCreateUserForms-Customizinglabels">Customizing
labels</h3><p>Tapestry makes it pretty easy to customize the labels used on the
fields. It's just a matter of creating a <em>message catalog</em> for the
page.</p><p>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.</p><p>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.</p><div
class="preformatted panel" style="border-width: 1px;"><div
class="preformattedHeader panelHeader" style="border-bottom-width:
1px;"><b>src/main/resources/com/example/tutorial/pages/address/CreateAddress.properties</b></div><div
class="preformattedContent panelContent">
+</div></div><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="492"
src="using-beaneditform-to-create-user-forms.data/create-address-reordered.png"></span></p><h3
id="UsingBeanEditFormToCreateUserForms-Customizinglabels">Customizing
labels</h3><p>Tapestry makes it pretty easy to customize the labels used on the
fields. It's just a matter of creating a <em>message catalog</em> for the
page.</p><p>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.</p><p>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.</p><div class="preformatte
d panel" style="border-width: 1px;"><div class="preformattedHeader
panelHeader" style="border-bottom-width:
1px;"><b>src/main/resources/com/example/tutorial/pages/address/CreateAddress.properties</b></div><div
class="preformattedContent panelContent">
<pre>street1-label=Street 1
street2-label=Street 2
email-label=E-Mail
zip-label=Zip Code
phone-label=Phone Number</pre>
-</div></div><p>Since this is a <em>new</em> file (and not a change to an
existing file), you may have to restart Jetty to force Tapestry to pick up the
change.</p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource
confluence-content-image-border"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v3.png?version=2&modificationDate=1417055915000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v3.png?version=2&modificationDate=1417055915000&api=v2"></span></p><p>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:</p><div class="preformatted
panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
+</div></div><p>Since this is a <em>new</em> file (and not a change to an
existing file), you may have to restart Jetty to force Tapestry to pick up the
change.</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="471"
src="using-beaneditform-to-create-user-forms.data/address-v3.png"></span></p><p>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:</p><div class="preformatted
panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>MR=Mr.
MRS=Mrs.
DR=Dr.
@@ -171,7 +171,7 @@ DR=Dr.
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:beaneditform submitlabel="Create Address"
object="address"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"/>
</pre>
-</div></div><p>The default for the submitlabel parameter is "Create/Update",
but here we're overriding that default to a specific value.</p><p>The final
result shows the reformatting and relabelling:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource confluence-content-image-border"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v5.png?version=2&modificationDate=1417055915000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v5.png?version=2&modificationDate=1417055915000&api=v2"></span><br
clear="none">Before continuing on to validation, a side note about message
catalogs. Message catalogs are not just for re-labeling fields and options;
we'll see in later chapters how message catalogs are used in the context of
localization and internationalization.</p><p>Instead of putting the label for
the submit button dir
ectly inside the template, we're going to provide a reference to the label;
the actual label will go in the message catalog.</p><p>In Tapestry, when
binding a parameter, the value you provide may include a prefix. The prefix
guides Tapestry in how to interpret the rest of the the parameter value ... is
it the name of a property? The id of a component? A message key? Most
parameters have a default prefix, usually "prop:", that is used when you fail
to provide one (this helps to make the templates as terse as
possible).</p><p>Here we want to reference a message from the catalog, so we
use the "message:" prefix:</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
+</div></div><p>The default for the submitlabel parameter is "Create/Update",
but here we're overriding that default to a specific value.</p><p>The final
result shows the reformatting and relabelling:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="453"
src="using-beaneditform-to-create-user-forms.data/address-v5.png"></span></p><p>Before
continuing on to validation, a side note about message catalogs. Message
catalogs are not just for re-labeling fields and options; we'll see in later
chapters how message catalogs are used in the context of localization and
internationalization.</p><p>Instead of putting the label for the submit button
directly inside the template, we're going to provide a reference to the label;
the actual label will go in the message catalog.</p><p>In Tapestry, when
binding a parameter, the value you provide may include a prefix. The prefix
guides Tapestry in how to interpret the res
t of the the parameter value ... is it the name of a property? The id of a
component? A message key? Most parameters have a default prefix, usually
"prop:", that is used when you fail to provide one (this helps to make the
templates as terse as possible).</p><p>Here we want to reference a message from
the catalog, so we use the "message:" prefix:</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:beaneditform object="address"
submitlabel="message:submit-label"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
/>
</pre>
@@ -182,14 +182,14 @@ DR=Dr.
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Validate("required")
public String firstName;
</pre>
-</div></div><p>What is that string, "required"? That's how you specify the
desired validation. It is a series of names that identify what type of
validation is desired. A number of validators are built in, such as "required",
"minLength" and "maxLength". As elsewhere, Tapestry is case
insensitive.</p><p>You can apply multiple validations, by separating the
validator names with commas. Some validators can be configured (with an equals
sign). Thus you might say "required,minLength=5" for a field that must be
specified, and must be at least five characters long.</p><div
class="confluence-information-macro confluence-information-macro-warning"><span
class="aui-icon aui-icon-small aui-iconfont-error
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>You can easily get confused when
you make a change to an entity class, such as adding the @Validate annotation,
and <em>not</em> see the result in the browser. Only component classes, and
(most) classe
s in the Tapestry services layer, are live-reloaded. Data and entity objects
are not reloaded, so this is one area where you need to stop and restart Jetty
to see the change.</p></div></div><p>Restart the application, and refresh your
browser, then hit the Create Address button.</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource confluence-content-image-border"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v6.png?version=3&modificationDate=1417056607000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v6.png?version=3&modificationDate=1417056607000&api=v2"></span></p><p>This
is a shot just after hitting the Create Address button; all the fields have
been validated and errors displayed. Each field in error has been highlighted
in red and had an error message added. Further, the label for each of the
fields has
also been highlighted in red, to even more clearly identify what's in error.
The cursor has also been moved to the first field that's in error. And
<em>all</em> of this is taking place on the client side, without any
communication with the application.</p><p>Once all the errors are corrected,
and the form does submit, all validations are performed on the server side as
well (just in case the client has JavaScript disabled).</p><p>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.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
+</div></div><p>What is that string, "required"? That's how you specify the
desired validation. It is a series of names that identify what type of
validation is desired. A number of validators are built in, such as "required",
"minLength" and "maxLength". As elsewhere, Tapestry is case
insensitive.</p><p>You can apply multiple validations, by separating the
validator names with commas. Some validators can be configured (with an equals
sign). Thus you might say "required,minLength=5" for a field that must be
specified, and must be at least five characters long.</p><div
class="confluence-information-macro confluence-information-macro-warning"><span
class="aui-icon aui-icon-small aui-iconfont-error
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>You can easily get confused when
you make a change to an entity class, such as adding the @Validate annotation,
and <em>not</em> see the result in the browser. Only component classes, and
(most) classe
s in the Tapestry services layer, are live-reloaded. Data and entity objects
are not reloaded, so this is one area where you need to stop and restart Jetty
to see the change.</p></div></div><p>Restart the application, and refresh your
browser, then hit the Create Address button.</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="452"
src="using-beaneditform-to-create-user-forms.data/address-v6.png"></span></p><p>This
is a shot just after hitting the Create Address button; all the fields have
been validated and errors displayed. Each field in error has been highlighted
in red and had an error message added. Further, the label for each of the
fields has also been highlighted in red, to even more clearly identify what's
in error. The cursor has also been moved to the first field that's in error.
And <em>all</em> of this is taking place on the client side, without any
communication with the application.</p><
p>Once all the errors are corrected, and the form does submit, all validations
are performed on the server side as well (just in case the client has
JavaScript disabled).</p><p>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.</p><div class="code panel pdl" style="border-width: 1px;"><div
class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Validate("required,regexp=^\\d{5}(-\\d{4})?$")
public String zip;
</pre>
-</div></div><p>Let's give it a try; restart the application and enter an "abc"
for the zip code.</p><p><span class="confluence-embedded-file-wrapper"><img
class="confluence-embedded-image confluence-external-resource
confluence-content-image-border"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v7.png?version=2&modificationDate=1417056608000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v7.png?version=2&modificationDate=1417056608000&api=v2"></span><br
clear="none">This is what you'll see after typing "abc" and clicking the
Create Address button.</p><div class="confluence-information-macro
confluence-information-macro-note"><span class="aui-icon aui-icon-small
aui-iconfont-warning confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>Modern browsers will automatically
validate a regexp field when the form is submitted, as shown above. O
lder browsers do not have that automatic support, but will still validate
input, using the same decorations as for the required fields in the previous
screenshot.</p></div></div><p>In any case, that's the right validation
behavior, but it's the wrong message. Your users are not going to know or care
about regular expressions.</p><p>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:</p><div class="preformatted panel" style="border-width:
1px;"><div class="preformattedContent panelContent">
+</div></div><p>Let's give it a try; restart the application and enter an "abc"
for the zip code.</p><p><span class="confluence-embedded-file-wrapper
confluence-embedded-manual-size"><img class="confluence-embedded-image"
width="448"
src="using-beaneditform-to-create-user-forms.data/address-v7.png"></span></p><p>This
is what you'll see after typing "abc" and clicking the Create Address
button.</p><div class="confluence-information-macro
confluence-information-macro-note"><span class="aui-icon aui-icon-small
aui-iconfont-warning confluence-information-macro-icon"></span><div
class="confluence-information-macro-body"><p>Modern browsers will automatically
validate a regexp field when the form is submitted, as shown above. Older
browsers do not have that automatic support, but will still validate input,
using the same decorations as for the required fields in the previous
screenshot.</p></div></div><p>In any case, that's the right validation
behavior, but it's the wrong message. Your use
rs are not going to know or care about regular expressions.</p><p>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:</p><div class="preformatted
panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>zip-regexp-message=Zip Codes are five or nine digits. Example: 02134 or
90125-1655.
</pre>
-</div></div><p>Refresh the page and submit again:</p><p><span
class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image
confluence-external-resource confluence-content-image-border"
src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v8.png?version=2&modificationDate=1417056608000&api=v2"
data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v8.png?version=2&modificationDate=1417056608000&api=v2"></span></p><p>This
trick isn't limited to just the regexp validator, it works equally well with
<em>any</em> validator.</p><p>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 expre
ssion to match against.</p><div class="code panel pdl" style="border-width:
1px;"><div class="codeContent panelContent pdl">
+</div></div><p>Refresh the page and submit again:</p><p><span
class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
class="confluence-embedded-image" width="455"
src="using-beaneditform-to-create-user-forms.data/address-v8.png"></span></p><p>This
trick isn't limited to just the regexp validator, it works equally well with
<em>any</em> validator.</p><p>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.</p><div class="code panel pdl"
style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Validate("required,regexp")
public String zip;
</pre>
@@ -197,7 +197,7 @@ DR=Dr.
<pre>zip-regexp=^\\d{5}(-\\d{4})?$
zip-regexp-message=Zip Codes are five or nine digits. Example: 02134 or
90125-1655.
</pre>
-</div></div><p>After a restart you'll see the ... the same behavior. But when
we start creating more complicated regular expressions, it'll be much, much
nicer to put them in the message catalog rather than inside the annotation
value. And inside the message catalog, you can change and tweak the regular
expressions without having to restart the application each time.</p><p>We could
go a bit further here, adding more regular expression validation for phone
numbers and e-mail addresses. We're also far from done in terms of further
customizations of the BeanEditForm component.</p><p>By now you are likely
curious about what happens <em>after</em> the form submits successfully
(without validation errors), so that's what we'll focus on next.</p><p>Next: <a
href="using-beaneditform-to-create-user-forms.html">Using BeanEditForm To
Create User Forms</a></p><p> </p><p></p></div>
+</div></div><p>After a restart you'll see the ... the same behavior. But when
we start creating more complicated regular expressions, it'll be much, much
nicer to put them in the message catalog rather than inside the annotation
value. And inside the message catalog, you can change and tweak the regular
expressions without having to restart the application each time.</p><p>We could
go a bit further here, adding more regular expression validation for phone
numbers and e-mail addresses. We're also far from done in terms of further
customizations of the BeanEditForm component.</p><p>By now you are likely
curious about what happens <em>after</em> the form submits successfully
(without validation errors), so that's what we'll focus on next.</p><p>Next: <a
href="using-tapestry-with-hibernate.html">Using Tapestry With
Hibernate</a></p><p> </p><p></p></div>
</div>
<div class="clearer"></div>