You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by bu...@apache.org on 2018/02/03 13:21:04 UTC

svn commit: r1024776 [5/5] - in /websites/production/tapestry/content: ./ cache/

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 13:21:04 2018
@@ -45,13 +45,26 @@
 
   <div class="wrapper bs">
 
-        <div id="navigation"><div class="nav"><ul class="alternate"><li><a  href="index.html">Home</a></li><li><a  href="getting-started.html">Getting Started</a></li><li><a  href="documentation.html">Documentation</a></li><li><a  href="download.html">Download</a></li><li><a  href="about.html">About</a></li><li><a  class="external-link" href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a  href="community.html">Community</a></li><li><a  class="external-link" href="http://www.apache.org/security/">Security</a></li><li><a  class="external-link" href="http://www.apache.org/">Apache</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div></div>
+        <div id="navigation"><div class="nav"><ul class="alternate"><li><a  href="index.html">Home</a></li><li><a  href="getting-started.html">Getting Started</a></li><li><a  href="documentation.html">Documentation</a></li><li><a  href="download.html">Download</a></li><li><a  href="about.html">About</a></li><li><a  class="external-link" href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a  href="community.html">Community</a></li><li><a  class="external-link" href="http://www.apache.org/security/">Security</a></li><li><a  class="external-link" href="http://www.apache.org/">Apache</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div>
+
+</div>
 
           <div id="top">
-            <div id="smallbanner"><div class="searchbox" style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999; font-size: 90%">Tapestry docs, issues, wikis &amp; blogs:</span><form enctype="application/x-www-form-urlencoded" method="get" action="http://tapestry.apache.org/search.html"> 
- <input type="text" name="q"> 
- <input type="submit" value="Search"> 
-</form></div><div class="emblem" style="float:left"><p><a  href="index.html"><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image confluence-external-resource" src="http://tapestry.apache.org/images/tapestry_small.png" data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div><div class="title" style="float:left; margin: 0 0 0 3em"><h1 id="SmallBanner-PageTitle">Using BeanEditForm To Create User Forms</h1></div></div>
+            <div id="smallbanner"><div class="searchbox" style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999; font-size: 90%">Tapestry docs, issues, wikis &amp; blogs:</span>
+<form enctype="application/x-www-form-urlencoded" method="get" action="http://tapestry.apache.org/search.html">
+  <input type="text" name="q">
+  <input type="submit" value="Search">
+</form>
+
+</div>
+
+
+<div class="emblem" style="float:left"><p><a  href="index.html"><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image confluence-external-resource" src="http://tapestry.apache.org/images/tapestry_small.png" data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div>
+
+
+<div class="title" style="float:left; margin: 0 0 0 3em"><h1 id="SmallBanner-PageTitle">Using BeanEditForm To Create User Forms</h1></div>
+
+</div>
       <div class="clearer"></div>
       </div>
 
@@ -113,7 +126,7 @@ public class CreateAddress
 
 }
 </pre>
-</div></div><p>So ... why is the class named "CreateAddress" and not simply "Create"? Actually, we could have named it "Create", and the application would still work, but the longer <em>class</em> name is equally valid. Tapestry noticed the redundancy in the class name (com.example.tutorial1.pages.<code><em>address</em></code>.Create<em>Address</em>) and just stripped out the redundant suffix.</p><p><span style="line-height: 1.4285715;">Tapestry actually creates a bunch of aliases for you pages; any of these aliases are valid and can appear in URLs or in the page parameter of PageLink. &#160;You can see the list in the console:</span></p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+</div></div><p>So ... why is the class named "CreateAddress" and not simply "Create"? Actually, we could have named it "Create", and the application would still work, but the longer <em>class</em> name is equally valid. Tapestry noticed the redundancy in the class name (com.example.tutorial1.pages.<code><em>address</em></code>.Create<em>Address</em>) and just stripped out the redundant suffix.</p><p><span>Tapestry actually creates a bunch of aliases for you pages; any of these aliases are valid and can appear in URLs or in the page parameter of PageLink. &#160;You can see the list in the console:</span></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;">[INFO] TapestryModule.ComponentClassResolver Available pages (12):
               (blank): com.example.tutorial1.pages.Index
    ComponentLibraries: org.apache.tapestry5.corelib.pages.ComponentLibraries
@@ -129,27 +142,27 @@ PropertyDisplayBlocks: org.apache.tapest
           T5Dashboard: org.apache.tapestry5.corelib.pages.T5Dashboard
        address/Create: com.example.tutorial1.pages.address.CreateAddress
 address/CreateAddress: com.example.tutorial1.pages.address.CreateAddress</pre>
-</div></div><p><span style="line-height: 1.4285715;">Tapestry users the shortest alias when constructing URLs.</span></p><p><span style="line-height: 1.4285715;">Eventually, your application will probably have more entities: perhaps you'll have a "user/Create" page and a "payment/Create" page and an "account/Create" page. You </span><em style="line-height: 1.4285715;">could</em><span style="line-height: 1.4285715;"> have a bunch of different classes all named Create spread across a number of different packages. That's legal Java, but it isn't ideal. You may find yourself accidentally editing the Java code for creating an Account when you really want to be editing the code for creating a Payment.</span></p><p>Tapestry is encouraging you to use a more descriptive name: Create<em>Address</em>, not just Create, but it isn't making you pay the cost (in terms of longer, uglier URLs). The URL to access the page will still be <a  class="external-link" href="http://localhost:8080/tutorial1/a
 ddress/create" rel="nofollow">http://localhost:8080/tutorial1/address/create</a>.</p><p>And remember, regardless of the name that Tapestry assigns to your page, the template file is named like the Java class itself: CreateAddress.tml.</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>Index pages work in folders as well. A class named com.example.tutorial1.pages.address.AddressIndex would be given the name "address/Index". However, Tapestry has special rules for pages named "Index" and the rendered URL would be <a  class="external-link" href="http://localhost:8080/tutorial1/address/" rel="nofollow">http://localhost:8080/tutorial1/address/</a>. In other words, you can place Index pages in any folder and Tapestry will build a short URL for that page ... and you <em>don't</em> have to keep naming the classe
 s Index (it's confusing to have many classes with the same name, even across multiple packages); instead, you can name each index page after the package that contains it. Tapestry users a smart <em>convention</em> to keep it all straight and generate short, to the point URLs.</p></div></div><h1 id="UsingBeanEditFormToCreateUserForms-UsingtheBeanEditFormComponent">Using the BeanEditForm Component</h1><p>Time to start putting together the logic for this form. Tapestry has a specific component for client-side Forms: the <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a> component, as well as components for form controls, such as <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Checkbox.html">Checkbox</a> and <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/TextField.html">T
 extField</a>. We'll cover those in a bit more detail later .. instead, we're again going to let Tapestry do the heavy lifting for us, via the <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/BeanEditForm.html">BeanEditForm</a> component.</p><p>Add the following to the CreateAddress template (replacing the "coming soon ..." message):</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><span>Tapestry users the shortest alias when constructing URLs.</span></p><p><span>Eventually, your application will probably have more entities: perhaps you'll have a "user/Create" page and a "payment/Create" page and an "account/Create" page. You </span><em>could</em><span> have a bunch of different classes all named Create spread across a number of different packages. That's legal Java, but it isn't ideal. You may find yourself accidentally editing the Java code for creating an Account when you really want to be editing the code for creating a Payment.</span></p><p>Tapestry is encouraging you to use a more descriptive name: Create<em>Address</em>, not just Create, but it isn't making you pay the cost (in terms of longer, uglier URLs). The URL to access the page will still be <a  class="external-link" href="http://localhost:8080/tutorial1/address/create" rel="nofollow">http://localhost:8080/tutorial1/address/create</a>.</p><p>And remember, regardless of the name tha
 t Tapestry assigns to your page, the template file is named like the Java class itself: CreateAddress.tml.</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>Index pages work in folders as well. A class named com.example.tutorial1.pages.address.AddressIndex would be given the name "address/Index". However, Tapestry has special rules for pages named "Index" and the rendered URL would be <a  class="external-link" href="http://localhost:8080/tutorial1/address/" rel="nofollow">http://localhost:8080/tutorial1/address/</a>. In other words, you can place Index pages in any folder and Tapestry will build a short URL for that page ... and you <em>don't</em> have to keep naming the classes Index (it's confusing to have many classes with the same name, even across multiple packages); instead, you can name each inde
 x page after the package that contains it. Tapestry users a smart <em>convention</em> to keep it all straight and generate short, to the point URLs.</p></div></div><h1 id="UsingBeanEditFormToCreateUserForms-UsingtheBeanEditFormComponent">Using the BeanEditForm Component</h1><p>Time to start putting together the logic for this form. Tapestry has a specific component for client-side Forms: the <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Form.html">Form</a> component, as well as components for form controls, such as <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Checkbox.html">Checkbox</a> and <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/TextField.html">TextField</a>. We'll cover those in a bit more detail later .. instead, we're again going to let Tapestry do the heavy lifting fo
 r us, via the <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/BeanEditForm.html">BeanEditForm</a> component.</p><p>Add the following to the CreateAddress template (replacing the "coming soon ..." message):</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;">    &lt;t:beaneditform object="address"/&gt;
 </pre>
 </div></div><p>And match that up with a property in the CreateAddress class:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>CreateAddress.java (partial)</b></div><div class="codeContent panelContent pdl">
 <pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">    @Property
     private Address address;
 </pre>
-</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" src="using-beaneditform-to-create-user-forms.data/hmac-warning.png"></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">
+</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&amp;modificationDate=1416883285000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/hmac-warning.png?version=2&amp;modificationDate=1416883285000&amp;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" 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 &lt;label&gt; 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>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 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"><img class="confluence-embedded-image confluence-external-resource" src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-initial.png?version=2&amp;modificationDate=1416884366000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-initial.png?version=2&amp;modificationDate=1416884366000&amp;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 &lt;label&gt; 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">
 <pre class="brush: xml; gutter: false; theme: Default" style="font-size:12px;">    &lt;t:beaneditform object="address"
         reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone" /&gt;
 </pre>
-</div></div><p><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image" 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="preformatted panel" style="border-width: 1px;"><div cla
 ss="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"><img class="confluence-embedded-image confluence-external-resource" src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-reordered.png?version=2&amp;modificationDate=1416884592000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/create-address-reordered.png?version=2&amp;modificationDate=1416884592000&amp;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">
 <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-content-image-border" 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">
+</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&amp;modificationDate=1417055915000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v3.png?version=2&amp;modificationDate=1417055915000&amp;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">
 <pre>MR=Mr.
 MRS=Mrs.
 DR=Dr.
@@ -158,7 +171,7 @@ DR=Dr.
 <pre class="brush: xml; gutter: false; theme: Default" style="font-size:12px;">    &lt;t:beaneditform submitlabel="Create Address" object="address"
         reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"/&gt;
 </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-content-image-border" src="using-beaneditform-to-create-user-forms.data/address-v5.png"></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 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 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"><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&amp;modificationDate=1417055915000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v5.png?version=2&amp;modificationDate=1417055915000&amp;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">
 <pre class="brush: xml; gutter: false; theme: Default" style="font-size:12px;">    &lt;t:beaneditform object="address" submitlabel="message:submit-label"
         reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone" /&gt;
 </pre>
@@ -169,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-content-image-border" 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 t
 he 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"><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&amp;modificationDate=1417056607000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v6.png?version=3&amp;modificationDate=1417056607000&amp;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">
 <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-content-image-border" src="using-beaneditform-to-create-user-forms.data/address-v7.png"></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. 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 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"><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&amp;modificationDate=1417056608000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v7.png?version=2&amp;modificationDate=1417056608000&amp;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">
 <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-content-image-border" 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">
+</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&amp;modificationDate=1417056608000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340431/address-v8.png?version=2&amp;modificationDate=1417056608000&amp;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">
 <pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">    @Validate("required,regexp")
     public String zip;
 </pre>
@@ -184,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-tapestry-with-hibernate.html">Using Tapestry With Hibernate</a></p><p>&#160;</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-beaneditform-to-create-user-forms.html">Using BeanEditForm To Create User Forms</a></p><p>&#160;</p><p></p></div>
       </div>
 
       <div class="clearer"></div>

Modified: websites/production/tapestry/content/using-tapestry-with-hibernate.html
==============================================================================
--- websites/production/tapestry/content/using-tapestry-with-hibernate.html (original)
+++ websites/production/tapestry/content/using-tapestry-with-hibernate.html Sat Feb  3 13:21:04 2018
@@ -45,13 +45,26 @@
 
   <div class="wrapper bs">
 
-        <div id="navigation"><div class="nav"><ul class="alternate"><li><a  href="index.html">Home</a></li><li><a  href="getting-started.html">Getting Started</a></li><li><a  href="documentation.html">Documentation</a></li><li><a  href="download.html">Download</a></li><li><a  href="about.html">About</a></li><li><a  class="external-link" href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a  href="community.html">Community</a></li><li><a  class="external-link" href="http://www.apache.org/security/">Security</a></li><li><a  class="external-link" href="http://www.apache.org/">Apache</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div></div>
+        <div id="navigation"><div class="nav"><ul class="alternate"><li><a  href="index.html">Home</a></li><li><a  href="getting-started.html">Getting Started</a></li><li><a  href="documentation.html">Documentation</a></li><li><a  href="download.html">Download</a></li><li><a  href="about.html">About</a></li><li><a  class="external-link" href="http://www.apache.org/licenses/LICENSE-2.0">License</a></li><li><a  href="community.html">Community</a></li><li><a  class="external-link" href="http://www.apache.org/security/">Security</a></li><li><a  class="external-link" href="http://www.apache.org/">Apache</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a  class="external-link" href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul></div>
+
+</div>
 
           <div id="top">
-            <div id="smallbanner"><div class="searchbox" style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999; font-size: 90%">Tapestry docs, issues, wikis &amp; blogs:</span><form enctype="application/x-www-form-urlencoded" method="get" action="http://tapestry.apache.org/search.html"> 
- <input type="text" name="q"> 
- <input type="submit" value="Search"> 
-</form></div><div class="emblem" style="float:left"><p><a  href="index.html"><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image confluence-external-resource" src="http://tapestry.apache.org/images/tapestry_small.png" data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div><div class="title" style="float:left; margin: 0 0 0 3em"><h1 id="SmallBanner-PageTitle">Using Tapestry With Hibernate</h1></div></div>
+            <div id="smallbanner"><div class="searchbox" style="float:right;margin: .3em 1em .1em 1em"><span style="color: #999; font-size: 90%">Tapestry docs, issues, wikis &amp; blogs:</span>
+<form enctype="application/x-www-form-urlencoded" method="get" action="http://tapestry.apache.org/search.html">
+  <input type="text" name="q">
+  <input type="submit" value="Search">
+</form>
+
+</div>
+
+
+<div class="emblem" style="float:left"><p><a  href="index.html"><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image confluence-external-resource" src="http://tapestry.apache.org/images/tapestry_small.png" data-image-src="http://tapestry.apache.org/images/tapestry_small.png"></span></a></p></div>
+
+
+<div class="title" style="float:left; margin: 0 0 0 3em"><h1 id="SmallBanner-PageTitle">Using Tapestry With Hibernate</h1></div>
+
+</div>
       <div class="clearer"></div>
       </div>
 
@@ -175,7 +188,7 @@ public class CreateAddress
     }
 }
 </pre>
-</div></div><p>The <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">Inject</a> annotation tells Tapestry to inject a service into the annotated field; Tapestry includes a sophisticated Inversion of Control container (similar in many ways to Spring) that is very good at locating available services by type, rather than by a string id. In any case, the Hibernate Session object is exposed as a Tapestry IoC service, ready to be injected (this is one of the things provided by the tapestry-hibernate module).</p><p>Tapestry automatically starts a transaction as necessary; however that transaction will be <em>aborted</em> at the end of the request by default. If we make changes to persistent objects, such as adding a new Address object, then it is necessary to commit the transaction.</p><p>The <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/hibernate/annotations/CommitA
 fter.html">CommitAfter</a> annotation can be applied to any component method; if the method completes normally, the transaction will be committed (and a new transaction started to replace the committed transaction).</p><p>After persisting the new address, we return to the main Index page of the application.</p><p><em>Note: In real applications, it is rare to have pages and components directly use the Hibernate Session. It is generally a better approach to define your own Data Access Object layer to perform common update operations and queries.</em></p><h2 id="UsingTapestryWithHibernate-ShowingAddresses">Showing Addresses</h2><p>As a little preview of what's next, let's display all the Addresses entered by the user on the Index page of the application. After you enter a few names, it will look something like:</p><p><span class="confluence-embedded-file-wrapper"><img class="confluence-embedded-image confluence-content-image-border" src="using-tapestry-with-hibernate.data/index-grid-v1
 .png"></span></p><h2 id="UsingTapestryWithHibernate-AddingtheGridtotheIndexpage">Adding the Grid to the Index page</h2><p>So, how is this implemented? Primarily, its accomplished by the <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Grid.html">Grid</a> component.</p><p>The Grid component is based on the same concepts as the BeanEditForm component; it can pull apart a bean into columns. The columns are sortable, and when there are more entries than will fit on a single page, page navigation is automatically added.</p><p>A minimal Grid is very easy to add to the template. Just add this near the bottom of Index.tml:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>src/main/webapp/Index.tml (partial)</b></div><div class="codeContent panelContent pdl">
+</div></div><p>The <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">Inject</a> annotation tells Tapestry to inject a service into the annotated field; Tapestry includes a sophisticated Inversion of Control container (similar in many ways to Spring) that is very good at locating available services by type, rather than by a string id. In any case, the Hibernate Session object is exposed as a Tapestry IoC service, ready to be injected (this is one of the things provided by the tapestry-hibernate module).</p><p>Tapestry automatically starts a transaction as necessary; however that transaction will be <em>aborted</em> at the end of the request by default. If we make changes to persistent objects, such as adding a new Address object, then it is necessary to commit the transaction.</p><p>The <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/hibernate/annotations/CommitA
 fter.html">CommitAfter</a> annotation can be applied to any component method; if the method completes normally, the transaction will be committed (and a new transaction started to replace the committed transaction).</p><p>After persisting the new address, we return to the main Index page of the application.</p><p><em>Note: In real applications, it is rare to have pages and components directly use the Hibernate Session. It is generally a better approach to define your own Data Access Object layer to perform common update operations and queries.</em></p><h2 id="UsingTapestryWithHibernate-ShowingAddresses">Showing Addresses</h2><p>As a little preview of what's next, let's display all the Addresses entered by the user on the Index page of the application. After you enter a few names, it will look something like:</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/23340507/index-grid-v1.png?version=4&amp;modificationDate=1418482289000&amp;api=v2" data-image-src="https://cwiki-test.apache.org/confluence/download/attachments/23340507/index-grid-v1.png?version=4&amp;modificationDate=1418482289000&amp;api=v2"></span></p><h2 id="UsingTapestryWithHibernate-AddingtheGridtotheIndexpage">Adding the Grid to the Index page</h2><p>So, how is this implemented? Primarily, its accomplished by the <a  class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Grid.html">Grid</a> component.</p><p>The Grid component is based on the same concepts as the BeanEditForm component; it can pull apart a bean into columns. The columns are sortable, and when there are more entries than will fit on a single page, page navigation is automatically added.</p><p>A minimal Grid is very easy to add to the template. Just add this near the bottom of Index.tml:</p><div class="code panel
  pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>src/main/webapp/Index.tml (partial)</b></div><div class="codeContent panelContent pdl">
 <pre class="brush: xml; gutter: false; theme: Default" style="font-size:12px;">  &lt;t:grid source="addresses"
          include="honorific,firstName,lastName,street1,city,state,zip,phone"/&gt;
 </pre>
@@ -195,7 +208,7 @@ public class Index
     }
 }
 </pre>
-</div></div><p>Here, we're using the Hibernate Session object to find all Address objects in the database. Any sorting that takes place will be done in memory. This is fine for now (with only a handful of Address objects in the database). Later we'll see how to optimize this for very large result sets.</p><h2 id="UsingTapestryWithHibernate-What'sNext?">What's Next?</h2><p>We have lots more to talk about: more components, more customizations, built-in Ajax support, more common design and implementation patterns, and even writing your own components (which is easy!).</p><p>Check out the many Tapestry resources available on the <a  href="documentation.html">Documentation</a> page, including the <a  href="getting-started.html">Getting Started</a> and <a  href="frequently-asked-questions.html">FAQ</a> pages and the <a  href="cookbook.html">Cookbook</a>. Be sure to peruse the <a  href="user-guide.html">User Guide</a>, which provides comprehensive details on nearly every Tapestry topic. Fi
 nally, be sure to visit (and bookmark) <a  class="external-link" href="http://jumpstart.doublenegative.com.au/jumpstart7/" rel="nofollow">Tapestry JumpStart</a>, which provides a nearly exhaustive set of tutorials.</p><p>&#160;</p><p></p></div>
+</div></div><p>Here, we're using the Hibernate Session object to find all Address objects in the database. Any sorting that takes place will be done in memory. This is fine for now (with only a handful of Address objects in the database). Later we'll see how to optimize this for very large result sets.</p><h2 id="UsingTapestryWithHibernate-What'sNext?">What's Next?</h2><p>We have lots more to talk about: more components, more customizations, built-in Ajax support, more common design and implementation patterns, and even writing your own components (which is easy!).</p><p>Check out the many Tapestry resources available on the <a  href="using-tapestry-with-hibernate.html">Using Tapestry With Hibernate</a> page, including the <a  href="using-tapestry-with-hibernate.html">Using Tapestry With Hibernate</a> and <a  href="using-tapestry-with-hibernate.html">FAQ</a> pages and the <a  href="using-tapestry-with-hibernate.html">Using Tapestry With Hibernate</a>. Be sure to peruse the <a  href=
 "using-tapestry-with-hibernate.html">Using Tapestry With Hibernate</a>, which provides comprehensive details on nearly every Tapestry topic. Finally, be sure to visit (and bookmark) <a  class="external-link" href="http://jumpstart.doublenegative.com.au/jumpstart7/" rel="nofollow">Tapestry JumpStart</a>, which provides a nearly exhaustive set of tutorials.</p><p>&#160;</p><p></p></div>
       </div>
 
       <div class="clearer"></div>