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
 Sun Apr 26 21:22:55 2020
@@ -77,7 +77,7 @@
 
       <div id="content">
                 <div id="ConfluenceContent"><p>In the previous chapters, we 
saw how Tapestry can handle simple links, even links that pass information in 
the URL. In this chapter, we'll see how Tapestry can do the same, and quite a 
bit more, for HTML forms.</p><p>Form support in Tapestry is deep and rich, more 
than can be covered in a single chapter. However, we can show the basics, 
including some very common development patterns. To get started, let's create a 
simple address book application.</p><p>We'll start with the entity data, a 
simple object to store the information we'll need. These classes go in an 
<code>entities</code> sub-package. Unlike the use of the <code>pages</code> 
sub-package (for page component classes), this is not enforced by Tapestry; 
it's just a convention (but as we'll see shortly, a handy one).</p><p>Tapestry 
treats public fields as if they were JavaBeans properties; since the Address 
object is just "dumb data", there's no need to get carried away writing ge
 tters and setters. Instead, we'll define an entity that is all public 
fields:</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>src/main/java/com/example/tutorial/entities/Address.java</b></div><div 
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">package com.example.tutorial1.entities;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">package 
com.example.tutorial1.entities;
 
 import com.example.tutorial1.data.Honorific;
 
@@ -96,7 +96,7 @@ public class Address
 }
 </pre>
 </div></div><p>We also need to define the enum type, Honorific:</p><div 
class="code panel pdl" style="border-width: 1px;"><div class="codeHeader 
panelHeader pdl" style="border-bottom-width: 
1px;"><b>src/main/java/com/example/tutorial/data/Honorific.java</b></div><div 
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">package com.example.tutorial1.data;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">package 
com.example.tutorial1.data;
 
 public enum Honorific
 {
@@ -104,14 +104,14 @@ public enum Honorific
 }
 </pre>
 </div></div><h1 id="UsingBeanEditFormToCreateUserForms-AddressPages">Address 
Pages</h1><p>We're probably going to create a few pages related to addresses: 
pages for creating them, for editing them, for searching and listing them. 
We'll create a sub-folder, address, to hold them. Let's get started on the 
first of these pages, "address/Create" (that's the real name, including the 
slash &#8212; we'll see in a minute how that maps to classes and 
templates).</p><p>First, we'll update the Index.tml template, to create a link 
to the new page:</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/Index.tml 
(partial)</b></div><div class="codeContent panelContent pdl">
-<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">    &lt;h1&gt;Address Book&lt;/h1&gt;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">    &lt;h1&gt;Address 
Book&lt;/h1&gt;
 
     &lt;ul&gt;
         &lt;li&gt;&lt;t:pagelink page="address/create"&gt;Create new 
address&lt;/t:pagelink&gt;&lt;/li&gt;
     &lt;/ul&gt;
 </pre>
 </div></div><p>Now we need the address/Create page; lets start with an empty 
shell, just to test our navigation.</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/address/CreateAddress.tml</b></div><div
 class="codeContent panelContent pdl">
-<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;html t:type="layout" title="Create New Address"
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">&lt;html t:type="layout" 
title="Create New Address"
     xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"&gt;
 
     &lt;em&gt;coming soon ...&lt;/em&gt;
@@ -119,7 +119,7 @@ public enum Honorific
 &lt;/html&gt;
 </pre>
 </div></div><p>(Note: for Tapestry 5.4, make that 
<code>tapestry_5_4.xsd</code> instead.)</p><p>Next, the corresponding 
class:</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>src/main/java/com/example/tutorial/pages/address/CreateAddress.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.address;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">package 
com.example.tutorial1.pages.address;
 
 public class CreateAddress
 {
@@ -127,7 +127,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>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):
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">[INFO] 
TapestryModule.ComponentClassResolver Available pages (12):
               (blank): com.example.tutorial1.pages.Index
    ComponentLibraries: org.apache.tapestry5.corelib.pages.ComponentLibraries
              Error404: com.example.tutorial1.pages.Error404
@@ -143,17 +143,17 @@ PropertyDisplayBlocks: org.apache.tapest
        address/Create: com.example.tutorial1.pages.address.CreateAddress
 address/CreateAddress: com.example.tutorial1.pages.address.CreateAddress</pre>
 </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 class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">    &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
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">    @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 
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
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">        // 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 
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 
&lt;label&gt; 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;">    &lt;t:beaneditform object="address"
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">    &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 
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">
@@ -168,29 +168,29 @@ MRS=Mrs.
 DR=Dr.
 </pre>
 </div></div><p>Notice that we don't have to include an option for MISS, 
because that is converted to "Miss" anyway. You might just want to include it 
for sake of consistency ... the point is, each option label is searched for 
separately.</p><p>Lastly, the default label on the submit button is 
"Create/Update" (BeanEditForm doesn't know how it is being used). Let's change 
that to "Create Address".</p><p>That button is a component within the 
BeanEditForm component. It's not a property, so we can't just put a message 
into the message catalog, the way we can with the fields. Fortunately, the 
BeanEditForm component includes a parameter expressly for re-labeling the 
button. Simply change the CreateAddress component template:</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 submitlabel="Create Address" 
object="address"
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">    &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 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;">    &lt;t:beaneditform object="address" 
submitlabel="message:submit-label"
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">    &lt;t:beaneditform 
object="address" submitlabel="message:submit-label"
         
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
 /&gt;
 </pre>
 </div></div><p>And then we define the submit-label key in the message 
catalog:</p><div class="preformatted panel" style="border-width: 1px;"><div 
class="preformattedContent panelContent">
 <pre>submit-label=Create Address
 </pre>
 </div></div><p>In the end, the exact same HTML is sent to the client, 
regardless of whether you include the label text directly in the template, or 
indirectly in the message catalog. In the long term, the latter approach will 
work better if you later chose to internationalize your application.</p><h3 
id="UsingBeanEditFormToCreateUserForms-AddingValidation">Adding 
Validation</h3><p>Before we worry about storing the Address object, we should 
make sure that the user provides reasonable values. For example, several of the 
fields should be required, and phone numbers and email address have specific 
formats.</p><p>The BeanEditForm checks for a Tapestry-specific annotation, @<a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/beaneditor/Validate.html";>Validate</a>,
 on the field, the getter method, or the setter method of each 
property.</p><p>Edit the Address entity, and update the lastName, firstName, 
street1, city, state and zip fields, adding a
  @Validate annotation to each:</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")
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">    @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 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})?$")
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">    
@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 
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 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")
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">    
@Validate("required,regexp")
     public String zip;
 </pre>
 </div></div><p>Now, just put the regular expression into the CreateAddress 
message catalog:</p><div class="preformatted panel" style="border-width: 
1px;"><div class="preformattedContent panelContent">

Modified: 
websites/production/tapestry/content/using-jsr-330-standard-annotations.html
==============================================================================
--- 
websites/production/tapestry/content/using-jsr-330-standard-annotations.html 
(original)
+++ 
websites/production/tapestry/content/using-jsr-330-standard-annotations.html 
Sun Apr 26 21:22:55 2020
@@ -90,41 +90,52 @@
 
 
 
+
+
 <h3>Related Articles</h3>
 
 <ul class="content-by-label"><li>
         <div>
-                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+                <span class="icon aui-icon content-type-page" 
title="Page">Page:</span>        </div>
 
         <div class="details">
-                        <a  href="injection-in-detail.html">Injection in 
Detail</a>
+                        <a  href="environmental-services.html">Environmental 
Services</a>
                 
                         
                     </div>
     </li><li>
         <div>
-                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+                <span class="icon aui-icon content-type-page" 
title="Page">Page:</span>        </div>
 
         <div class="details">
-                        <a  href="injection-faq.html">Injection FAQ</a>
+                        <a  href="injection-in-detail.html">Injection in 
Detail</a>
                 
                         
                     </div>
     </li><li>
         <div>
-                <span class="icon aui-icon aui-icon-small 
aui-iconfont-page-default" title="Page">Page:</span>        </div>
+                <span class="icon aui-icon content-type-page" 
title="Page">Page:</span>        </div>
 
         <div class="details">
                         <a  href="injection.html">Injection</a>
                 
                         
                     </div>
+    </li><li>
+        <div>
+                <span class="icon aui-icon content-type-page" 
title="Page">Page:</span>        </div>
+
+        <div class="details">
+                        <a  href="injection-faq.html">Injection FAQ</a>
+                
+                        
+                    </div>
     </li></ul>
 </div>
 
 
 <p>The following table demonstrates that most of annotations in Tapestry IoC 
and JSR-330 are interchangeable. However, there are few differences in 
semantics.</p><div class="table-wrap"><table 
class="confluenceTable"><tbody><tr><th colspan="1" rowspan="1" 
class="confluenceTh"><p>JSR-330 <br clear="none" class="atl-forced-newline"> 
javax.inject</p></th><th colspan="1" rowspan="1" 
class="confluenceTh"><p>Tapestry <br clear="none" class="atl-forced-newline"> 
org.apache.tapestry5.ioc.annotations</p></th><th colspan="1" rowspan="1" 
class="confluenceTh"><p>Comment</p></th></tr><tr><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@Inject</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@Inject</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>-</p></td></tr><tr><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@Inject <br clear="none" class="atl-forced-newline"> 
@Named</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@InjectService</p></td><td colspan="
 1" rowspan="1" class="confluenceTd"><p>-</p></td></tr><tr><td colspan="1" 
rowspan="1" class="confluenceTd"><p>@Scope</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@Scope</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>-</p></td></tr><tr><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@Qualifier</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>-</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>Tapestry marker annotations don&#8217;t need any 
qualifier annotations</p></td></tr><tr><td colspan="1" rowspan="1" 
class="confluenceTd"><p>@Singleton</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>-</p></td><td colspan="1" rowspan="1" 
class="confluenceTd"><p>By default all Tapestry services are 
singletons</p></td></tr></tbody></table></div><h1 
id="UsingJSR330standardannotations-FieldInjection">Field 
Injection</h1><p>Let&#8217;s start with field injection. In Tapestry the 
injection into fields is triggered by&#160;<a  class="exter
 nal-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html";>@Inject</a>&#160;or&#160;<a
  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html";>@InjectService</a>&#160;annotations.
 When @Inject annotation is present on a field, Tapestry tries to resolve the 
object to inject by the type of the field. If several implementations of the 
same service interface are available in the registry, you have to disambiguate 
which implementation you want to be injected. This can be done by placing 
the&#160;<a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html";>@InjectService</a>&#160;annotation
 on the injection point.</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;">import org.apache.tapestry5.ioc.annotations.Inject;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">import 
org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.annotations.InjectService;
 
 ...
@@ -144,7 +155,7 @@ public class AuthenticationFilter implem
 
 }</pre>
 </div></div><p>Now let&#8217;s see the JSR-330 equivalent of the same service. 
As you can see the @Inject annotations are interchangeable. The difference is 
how to get a service by its unique id. For this purpose JSR-330 provides 
the&#160;<a  class="external-link" 
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Named.html"; 
rel="nofollow">@Named</a>&#160;annotation which accompanies the @Inject 
annotation.</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;">import javax.inject.Inject;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">import javax.inject.Inject;
 import javax.inject.Named;
 
 ...
@@ -164,7 +175,7 @@ public class AuthenticationFilter implem
 
 }</pre>
 </div></div><h1 
id="UsingJSR330standardannotations-ConstructorInjection">Constructor 
Injection</h1><p>For constructor injection the @Inject annotations are 
interchangeable. You can use either JSR-330 or Tapestry annotation to mark a 
constructor for injection. Note that at most one constructor per class may be 
marked as injection point.</p><p>However, the semantics of constructor 
injection are different in JSR-330 and Tapestry IoC. In JSR-330 a constructor 
is injectable only if the @Inject annotation is present.</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;">public class Car {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class Car {
 
    public Car() { ... }
 
@@ -172,7 +183,7 @@ public class AuthenticationFilter implem
    public Car(Engine engine) { ... }
 }</pre>
 </div></div><p>In Tapestry the @Inject annotation for constructors is 
optional. All available constructors are candidates for injection: the 
constructor with the most parameters will be invoked.</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;">public class Car {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class Car {
 
    public Car() { ... }
 
@@ -180,7 +191,7 @@ public class AuthenticationFilter implem
 
 }</pre>
 </div></div><p>When several constructors are available and you don&#8217;t 
want the constructor with most&#160; parameters to be injectable, you need to 
place the&#160;<a  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html";>@Inject</a>&#160;annotation.</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;">public class Car {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class Car {
 
    public Car() { ... }
 
@@ -191,7 +202,7 @@ public class AuthenticationFilter implem
 
 }</pre>
 </div></div><h1 
id="UsingJSR330standardannotations-InjectionIntoPagesandComponents">Injection 
Into Pages and Components</h1><p>Inside Tapestry components, injection occurs 
exclusively on fields. So far the injection was triggered by the&#160;<a  
class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html";>@Inject</a>&#160;or&#160;<a
  class="external-link" 
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html";>@InjectService</a>&#160;annotations.
 As of version 5.3 the injection points can also be marked with JSR-330 
annotations. The following example demonstrates that.</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;">public class Index {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class Index {
 
    @Inject
    private Request request;
@@ -211,7 +222,7 @@ public class AuthenticationFilter implem
 
 }</pre>
 </div></div><h1 
id="UsingJSR330standardannotations-Marker/QualifierAnnotations">Marker/Qualifier
 Annotations</h1><p>Both JSR-330 and Tapestry IoC allow you to disambiguate 
services by marker or qualifier annotations, as shown in the following 
example.</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;">public class Index {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class Index {
 
    @Inject
    @French
@@ -219,14 +230,14 @@ public class AuthenticationFilter implem
 
 }</pre>
 </div></div><p>Again, there is a slight difference. In JSR-330 a qualifier 
annotation like&#160;<em>@French</em>&#160;in the example above needs to be 
annotated by the&#160;<a  class="external-link" 
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Qualifier.html";
 rel="nofollow">@Qualifier</a>&#160;annotation.</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;">@Documented
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">@Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
 @javax.inject.Qualifier
 public @interface French {
 }</pre>
 </div></div><p>In Tapestry any annotation can be a marker annotation. You 
don&#8217;t need to place something like the&#160;<a  class="external-link" 
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Qualifier.html";
 rel="nofollow">@Qualifier</a>&#160;annotation on your marker 
annotation.</p><h1 id="UsingJSR330standardannotations-MethodInjection">Method 
Injection</h1><p>Injectable methods is a next slight difference. In JSR-330 a 
method is injectable if the @Inject annotation is present. In Tapestry the 
@Inject annotation is optional. An ordinary setter method is a candidate to 
perform injection.</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;">public class Car {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class Car {
 
    private Engine engine;
 
@@ -236,7 +247,7 @@ public @interface French {
 
 }</pre>
 </div></div><p>When building a&#160;<em>Car</em>&#160;instance, Tapestry IoC 
will try to resolve a service of type&#160;<em>Engine</em>. If available, 
Tapestry will perform injection by invoking the setter method.</p><p>Besides 
that, module methods are injectable. Again, there is no need to mark the 
methods with @Inject annotation as Tapestry explicitly knows which module 
methods to invoke. In the following example you can see how to use&#160;<a  
class="external-link" 
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Named.html"; 
rel="nofollow">@Named</a>&#160;annotation to inject a service by id into 
a&#160;<em>contribute method</em>.</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;">public class TapestryModule {
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">public class TapestryModule 
{
 
    @Contribute(BindingSource.class)
    public static void provideBindings(

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 Sun 
Apr 26 21:22:55 2020
@@ -77,7 +77,7 @@
 
       <div id="content">
                 <div id="ConfluenceContent"><p>So, you fill in all the fields, 
submit the form (without validation errors) and voila: you get back the same 
form, blanked out. What happened, and where did the data go?</p><p>What 
happened is that we haven't told Tapestry what to do after the form is 
successfully submitted (by successful, we mean, with no validation errors). 
Tapestry's default behavior is to redisplay the active page, and that occurs in 
a new request, with a new instance of the Address object (because the address 
field is not a peristent field).</p><p>Well, since we're creating objects, we 
might as well store them somewhere ... in a database. We're going to quickly 
integrate Tapestry with <a  class="external-link" href="http://hibernate.org"; 
rel="nofollow">Hibernate</a> as the object/relational mapping layer, and 
ultimately store our data inside a <a  class="external-link" 
href="http://www.hsqldb.org/"; rel="nofollow">HyperSQL</a> (HSQLDB) database. 
HSQLDB is an embedde
 d database engine and requires no installation &#8211; it will be pulled down 
as a dependency by Maven.</p><h2 
id="UsingTapestryWithHibernate-Re-configuringtheProject">Re-configuring the 
Project</h2><p>We're going to bootstrap this project from a simple Tapestry 
project to one that uses Hibernate and HSQLDB.</p><h3 
id="UsingTapestryWithHibernate-UpdatingtheDependencies">Updating the 
Dependencies</h3><p>First, we must update the POM to list a new set of 
dependencies, that includes Hibernate, the Tapestry/Hibernate integration 
library, and the HSQLDB JDBC driver:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 1px;"><b>src/pom.xml (partial)</b></div><div 
class="codeContent panelContent pdl">
-<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">    &lt;dependencies&gt;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">    &lt;dependencies&gt;
 
         &lt;dependency&gt;
             &lt;groupId&gt;org.apache.tapestry&lt;/groupId&gt;
@@ -94,7 +94,7 @@
     &lt;/dependencies&gt;
 </pre>
 </div></div><p>The tapestry-hibernate library includes, as transitive 
dependencies, Hibernate and tapestry-core. This means that you can simply 
replace "tapestry-core" with "tapestry-hibernate" inside the &lt;artifactId&gt; 
element.</p><p>After changing the POM and saving, Maven should automatically 
download the JARs for the new dependencies.</p><h3 
id="UsingTapestryWithHibernate-HibernateConfiguration">Hibernate 
Configuration</h3><p>Hibernate needs a master configuration file, 
hibernate.cfg.xml, used to store connection and other data. Create this in your 
src/main/resources folder:</p><div class="code panel pdl" style="border-width: 
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>src/main/resources/hibernate.cfg.xml</b></div><div class="codeContent 
panelContent pdl">
-<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">&lt;!DOCTYPE hibernate-configuration PUBLIC
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">&lt;!DOCTYPE 
hibernate-configuration PUBLIC
         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&gt;
 &lt;hibernate-configuration&gt;
@@ -111,7 +111,7 @@
 &lt;/hibernate-configuration&gt;
 </pre>
 </div></div><p>Most of the configuration is to identify the JDBC driver and 
connection URL.</p><p>Note the connection URL. We are instructing HSQLDB to 
store its database files within our project's target directory. We are also 
instructing HSQLDB to flush any data to these files at shutdown. This means 
that data will persist across different invocations of this project, but if the 
target directory is destroyed (e.g., via "mvn clean"), then all the database 
contents will be lost.</p><p>In addition, we are configuring Hibernate to 
<em>update</em> the database schema; when Hibernate initializes it will create 
or even modify tables to match the entities. Finally, we are configuring 
Hibernate to output any SQL it executes, which is very useful when initially 
building an application.</p><p>But what entities? Normally, the available 
entities are listed inside hibernate.cfg.xml, but that's not necessary with 
Tapestry; in another example of convention over configuration, Tapestry locates 
all
  entity classes inside the entities package ("com.example.tutorial1.entities" 
in our case) and adds them to the configuration. Currently, that is just the 
Address entity.</p><h2 
id="UsingTapestryWithHibernate-AddingHibernateAnnotations">Adding Hibernate 
Annotations</h2><p>For an entity class to be used with Hibernate, some 
Hibernate annotations must be added to the class.</p><p>Below is the updated 
Address class, with the Hibernate annotations (as well as the Tapestry 
ones).</p><div class="code panel pdl" style="border-width: 1px;"><div 
class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>src/main/java/com/example/tutorial/entities/Address.java</b></div><div 
class="codeContent panelContent pdl">
-<pre class="brush: java; gutter: false; theme: Default" 
style="font-size:12px;">package com.example.tutorial1.entities;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">package 
com.example.tutorial1.entities;
 
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
@@ -158,7 +158,7 @@ public class Address
 }
 </pre>
 </div></div><p>The Tapestry annotations, @NonVisual and @Validate, may be 
placed on the setter or getter method or on the field (as we have done here). 
As with the Hibernate annotations, putting the annotation on the field requires 
that the field name match the corresponding property 
name.</p><ul><li><strong>@NonVisual</strong> &#8211; indicates a field, such as 
a primary key, that should not be made visible to the 
user.</li><li><strong>@Validate</strong> &#8211; identifies the validations 
associated with a field.</li></ul><p>At this point you should stop and restart 
your application.</p><h2 
id="UsingTapestryWithHibernate-UpdatingtheDatabase">Updating the 
Database</h2><p>So we have a database set up, and Hibernate is configured to 
connect to it. Let's make use of that to store our Address object in the 
database.</p><p>What we need is to provide some code to be executed when the 
form is submitted. When a Tapestry form is submitted, there is a whole series 
of events that get fired. Th
 e event we are interested in is the "success" event, which comes late in the 
process, after all the values have been pulled out of the request and applied 
to the page properties, and after all server-side validations have 
occurred.</p><p>The success event is only fired if there are no validation 
errors.</p><p>Our event handler must do two things:</p><ul><li>Use the 
Hibernate Session object to persist the new Address object.</li><li>Commit the 
transaction to force the data to be written to the database.</li></ul><p>Let's 
update our CreateAddress.java class:</p><div class="code panel pdl" 
style="border-width: 1px;"><div class="codeHeader panelHeader pdl" 
style="border-bottom-width: 
1px;"><b>src/main/java/com/example/tutorial/pages/address/CreateAddress.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.address;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">package 
com.example.tutorial1.pages.address;
 
 import com.example.tutorial1.entities.Address;
 import com.example.tutorial1.pages.Index;
@@ -189,11 +189,11 @@ 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 
confluence-embedded-manual-size"><img class="confluence-embedded-image" 
width="722" src="using-tapestry-with-hibernate.data/i
 ndex-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">
-<pre class="brush: xml; gutter: false; theme: Default" 
style="font-size:12px;">  &lt;t:grid source="addresses"
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: xml; 
gutter: false; theme: Default" data-theme="Default">  &lt;t:grid 
source="addresses"
          
include="honorific,firstName,lastName,street1,city,state,zip,phone"/&gt;
 </pre>
 </div></div><p>Note that the Grid component accepts many of the same 
parameters that we used with the BeanEditForm. Here we use the include 
parameter to specify the properties to show, and in what order.</p><p>Now all 
we have to do is supply the addresses property in the Java code. Here's how 
Index.java should look now:</p><div class="code panel pdl" style="border-width: 
1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 
1px;"><b>src/main/java/com/example/tutorial/pages/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;
+<pre class="syntaxhighlighter-pre" data-syntaxhighlighter-params="brush: java; 
gutter: false; theme: Default" data-theme="Default">package 
com.example.tutorial1.pages;
 import java.util.List;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.hibernate.Session;

Modified: websites/production/tapestry/content/whatistapestry-v2.html
==============================================================================
--- websites/production/tapestry/content/whatistapestry-v2.html (original)
+++ websites/production/tapestry/content/whatistapestry-v2.html Sun Apr 26 
21:22:55 2020
@@ -67,11 +67,7 @@
       </div>
 
       <div id="content">
-                <div id="ConfluenceContent"><style type="text/css">
-#whatIs h4 { padding-left: 45px; }#is-polyglot { background : 
url("whatistapestry.data/Chat.png") no-repeat scroll left top transparent; 
}#is-injection { background : url("whatistapestry.data/injection.png") 
no-repeat scroll left top transparent; }#is-productive { background : 
url("whatistapestry.data/Refresh.png") no-repeat scroll left top transparent; 
}#is-testable { background : url("whatistapestry.data/accepted.png") no-repeat 
scroll left top transparent; }#is-fast { background : 
url("whatistapestry.data/fast.png") no-repeat scroll left top transparent; 
}#is-reporting { background : url("whatistapestry.data/bug.png") no-repeat 
scroll left top transparent; }#is-scalable { background : 
url("whatistapestry.data/Web.png") no-repeat scroll left top transparent; 
}#is-modular { background : url("whatistapestry.data/plugin.png") no-repeat 
scroll left top transparent; }
-</style>
-<br clear="none">
-<div class="row" id="whatIs"><h3 id="whatIsTapestry-v2-WhatisTapestry?">What 
is Tapestry?</h3><div class="col-md-6"><div id="is-polyglot"><h4 
id="whatIsTapestry-v2-PureJavaandPolyglot">Pure Java and 
Polyglot</h4><p>Written in pure Java: code your pages and components in Java, 
Groovy or Scala.</p></div>
+                <div id="ConfluenceContent"><div class="error"><span 
class="error">Error</span> CSS Stylesheet macro - URL 
'whatistapestry.data/Chat.png' is not whitelisted. If you want to include this 
content, contact your Confluence administrator to request <a  
href="https://confluence.atlassian.com/doc/configuring-the-whitelist-381255821.html";>whitelisting</a>
 this URL.</div><div class="row" id="whatIs"><h3 
id="whatIsTapestry-v2-WhatisTapestry?">What is Tapestry?</h3><div 
class="col-md-6"><div id="is-polyglot"><h4 
id="whatIsTapestry-v2-PureJavaandPolyglot">Pure Java and 
Polyglot</h4><p>Written in pure Java: code your pages and components in Java, 
Groovy or Scala.</p></div>
 
 
 <div id="is-productive"><h4 id="whatIsTapestry-v2-HighlyProductive">Highly 
Productive</h4><p>Live class reloading means that the time between seeing an 
error and providing the fix is seconds, not minutes.</p></div>


Reply via email to