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
Wed Sep 20 12:29:16 2017
@@ -27,6 +27,15 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
+ <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
+ <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
+ <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
+ <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
+ <script src='/resources/highlighter/scripts/shBrushXml.js'
type='text/javascript'></script>
+ <script>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -36,26 +45,13 @@
<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 & 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 & 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>
@@ -67,7 +63,8 @@
</div>
<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><parameter ac:name="language">java</parameter><parameter
ac:name="title">src/main/java/com/example/tutorial/entities/Address.java</parameter><plain-text-body>package
com.example.tutorial1.entities;
+ <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;
import com.example.tutorial1.data.Honorific;
@@ -84,30 +81,40 @@ public class Address
public String email;
public String phone;
}
-</plain-text-body><p>We also need to define the enum type,
Honorific:</p><parameter ac:name="language">java</parameter><parameter
ac:name="title">src/main/java/com/example/tutorial/data/Honorific.java</parameter><plain-text-body>package
com.example.tutorial1.data;
+</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;
public enum Honorific
{
MR, MRS, MISS, DR
}
-</plain-text-body><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 —
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><parameter ac:name="language">xml</parameter><parameter
ac:name="title">src/main/resources/com/example/tutorial/pages/Index.tml
(partial)</parameter><plain-text-body> <h1>Address Book</h1>
+</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 — 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;"> <h1>Address Book</h1>
<ul>
<li><t:pagelink page="address/create">Create new
address</t:pagelink></li>
</ul>
-</plain-text-body><p>Now we need the address/Create page; lets start with an
empty shell, just to test our navigation.</p><parameter
ac:name="language">xml</parameter><parameter
ac:name="title">src/main/resources/com/example/tutorial/pages/address/CreateAddress.tml</parameter><plain-text-body><html
t:type="layout" title="Create New Address"
+</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;"><html t:type="layout" title="Create New Address"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
<em>coming soon ...</em>
</html>
-</plain-text-body><p>(Note: for Tapestry 5.4, make that
<code>tapestry_5_4.xsd</code> instead.)</p><p>Next, the corresponding
class:</p><parameter ac:name="language">java</parameter><parameter
ac:name="title">src/main/java/com/example/tutorial/pages/address/CreateAddress.java</parameter><plain-text-body>package
com.example.tutorial1.pages.address;
+</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;
public class CreateAddress
{
}
-</plain-text-body><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.  You can see the list in the
console:</span></p><plain-text-body>[INFO]
TapestryModule.ComponentClassResolver Available pages (12):
+</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.  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
Error404: com.example.tutorial1.pages.Error404
@@ -121,34 +128,63 @@ PropertyDisplayBlocks: org.apache.tapest
ServiceStatus: org.apache.tapestry5.corelib.pages.ServiceStatus
T5Dashboard: org.apache.tapestry5.corelib.pages.T5Dashboard
address/Create: com.example.tutorial1.pages.address.CreateAddress
-address/CreateAddress:
com.example.tutorial1.pages.address.CreateAddress</plain-text-body><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 w
ill 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 that Tapestry assigns to your page, the
template file is named like the Java class itself:
CreateAddress.tml.</p><rich-text-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 index pag
e 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></rich-text-body><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><parameter
ac:name="language">xml</parameter><parameter ac:name="title">CreateAddress.tml
(partial)</parameter><plain-text-body> <t:beaneditform
object="address"/>
-</plain-text-body><p>And match that up with a property in the CreateAddress
class:</p><parameter ac:name="language">java</parameter><parameter
ac:name="title">CreateAddress.java (partial)</parameter><plain-text-body>
@Property
+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">
+<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:beaneditform object="address"/>
+</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;
-</plain-text-body><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><plain-text-body> // Set the HMAC pass phrase to secure
object data serialized to client
- configuration.add(SymbolConstants.HMAC_PASSPHRASE,
"");</plain-text-body><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
<label> element
s, 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> parame
ter of the BeanEditForm component, which is a comma separated list of property
(or public field) names:</p><parameter
ac:name="language">xml</parameter><parameter ac:name="title">CreateAddress.tml
(partial)</parameter><plain-text-body> <t:beaneditform object="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">
+<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
<label> elements, so clicking a label with the mouse will move the input
cursor into
the corresponding field.</p><p>This is an awesome start; it's a presentable
interface, quite nice in fact for a few minute's work. But it's far from
perfect; let's get started with some customizations.</p><h1
id="UsingBeanEditFormToCreateUserForms-ChangingFieldOrder">Changing Field
Order</h1><p>The BeanEditForm must guess at the right order to present the
fields; for public fields, they end up in alphabetical order. For standard
JavaBeans properties, the BeanEditForm default is in the order in which the
getter methods are defined in the class (it uses line number information, if
available).</p><p>A better order for these fields is the order in which they
are defined in the Address
class:</p><ul><li>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">
+<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:beaneditform object="address"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
/>
-</plain-text-body><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><parameter
ac:name="title">src/main/resources/com/example/tut
orial/pages/address/CreateAddress.properties</parameter><plain-text-body>street1-label=Street
1
+</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">
+<pre>street1-label=Street 1
street2-label=Street 2
email-label=E-Mail
zip-label=Zip Code
-phone-label=Phone Number</plain-text-body><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><plain-text-body>MR=Mr.
+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">
+<pre>MR=Mr.
MRS=Mrs.
DR=Dr.
-</plain-text-body><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><parameter
ac:name="language">xml</parameter><plain-text-body> <t:beaneditform
submitlabel="Create Address" object="address"
+</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;"> <t:beaneditform submitlabel="Create Address"
object="address"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"/>
-</plain-text-body><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><parameter
ac:name="language">xml</parameter><plain-text-body> <t:beaneditform
object="address" submitlabel="message:submit-label"
+</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">
+<pre class="brush: xml; gutter: false; theme: Default"
style="font-size:12px;"> <t:beaneditform object="address"
submitlabel="message:submit-label"
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
/>
-</plain-text-body><p>And then we define the submit-label key in the message
catalog:</p><plain-text-body>submit-label=Create Address
-</plain-text-body><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, ad
ding a @Validate annotation to each:</p><parameter
ac:name="language">java</parameter><plain-text-body> @Validate("required")
+</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")
public String firstName;
-</plain-text-body><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><rich-text-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) classes 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></rich-text-body><p>Rest
art 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 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 inter
esting 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><parameter
ac:name="language">java</parameter><plain-text-body>
@Validate("required,regexp=^\\d{5}(-\\d{4})?$")
+</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">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Validate("required,regexp=^\\d{5}(-\\d{4})?$")
public String zip;
-</plain-text-body><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><rich-text-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></rich-text-body><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><plain-text-body>zip-regexp-message=Zip Codes are five or nine
digits. Example: 02134 or 90125-1655.
-</plain-text-body><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><parameter
ac:name="language">java</parameter><plain-text-body>
@Validate("required,regexp")
+</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">
+<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">
+<pre class="brush: java; gutter: false; theme: Default"
style="font-size:12px;"> @Validate("required,regexp")
public String zip;
-</plain-text-body><p>Now, just put the regular expression into the
CreateAddress message catalog:</p><plain-text-body>zip-regexp=^\\d{5}(-\\d{4})?$
+</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">
+<pre>zip-regexp=^\\d{5}(-\\d{4})?$
zip-regexp-message=Zip Codes are five or nine digits. Example: 02134 or
90125-1655.
-</plain-text-body><p>After a restart you'll see the ... the same behavior. But
when we start creating more complicated regular expressions, it'll be much,
much nicer to put them in the message catalog rather than inside the annotation
value. And inside the message catalog, you can change and tweak the regular
expressions without having to restart the application each time.</p><p>We could
go a bit further here, adding more regular expression validation for phone
numbers and e-mail addresses. We're also far from done in terms of further
customizations of the BeanEditForm component.</p><p>By now you are likely
curious about what happens <em>after</em> the form submits successfully
(without validation errors), so that's what we'll focus on next.</p><p>Next: <a
href="using-tapestry-with-hibernate.html">Using Tapestry With
Hibernate</a></p><p> </p><p></p></div>
+</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> </p><p></p></div>
</div>
<div class="clearer"></div>
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
Wed Sep 20 12:29:16 2017
@@ -27,6 +27,14 @@
</title>
<link type="text/css" rel="stylesheet" href="/resources/space.css" />
+ <link href='/resources/highlighter/styles/shCoreCXF.css'
rel='stylesheet' type='text/css' />
+ <link href='/resources/highlighter/styles/shThemeCXF.css' rel='stylesheet'
type='text/css' />
+ <script src='/resources/highlighter/scripts/shCore.js'
type='text/javascript'></script>
+ <script src='/resources/highlighter/scripts/shBrushJava.js'
type='text/javascript'></script>
+ <script>
+ SyntaxHighlighter.defaults['toolbar'] = false;
+ SyntaxHighlighter.all();
+ </script>
<link href="/styles/style.css" rel="stylesheet" type="text/css"/>
@@ -72,7 +80,50 @@
</p><div class="confluence-information-macro
confluence-information-macro-information"><p class="title">Added in
5.3</p><span class="aui-icon aui-icon-small aui-iconfont-info
confluence-information-macro-icon"></span><div
class="confluence-information-macro-body">
</div></div>
<div class="error"><span class="error">Unknown macro: {div}</span>
-<p> </p></div><strong>JSR-330 annotations</strong> can be used for
injection in Tapestry 5.3 and later.<parameter
ac:name="style">float:right</parameter><parameter ac:name="title">Related
Articles</parameter><parameter
ac:name="class">aui-label</parameter><rich-text-body><parameter
ac:name="showLabels">false</parameter><parameter
ac:name="showSpace">false</parameter><parameter ac:name="title">Related
Articles</parameter><parameter ac:name="cql">label = "injection" and space =
currentSpace()</parameter></rich-text-body><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.apac
he.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 anno
tations don’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’s start with field injection. In Tapestry the
injection into fields is triggered by <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a> or <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">@InjectService</a> 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 servic
e interface are available in the registry, you have to disambiguate which
implementation you want to be injected. This can be done by placing the <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">@InjectService</a> annotation
on the injection point.</p><plain-text-body>import
org.apache.tapestry5.ioc.annotations.Inject;
+<p> </p></div><strong>JSR-330 annotations</strong> can be used for
injection in Tapestry 5.3 and later.<div class="aui-label" style="float:right"
title="Related Articles">
+
+
+
+
+
+
+
+
+<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>
+
+ <div class="details">
+ <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>
+
+ <div class="details">
+ <a href="injection-faq.html">Injection FAQ</a>
+
+
+ </div>
+ </li><li>
+ <div>
+ <span class="icon aui-icon aui-icon-small
aui-iconfont-page-default" title="Page">Page:</span> </div>
+
+ <div class="details">
+ <a href="injection.html">Injection</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’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’s start with field injection. In Tapestry the
injection into fields is triggered by <a class="exter
nal-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a> or <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">@InjectService</a> 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 <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">@InjectService</a> 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;
import org.apache.tapestry5.ioc.annotations.InjectService;
...
@@ -90,7 +141,9 @@ public class AuthenticationFilter implem
...
-}</plain-text-body><p>Now let’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 <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Named.html"
rel="nofollow">@Named</a> annotation which accompanies the @Inject
annotation.</p><plain-text-body>import javax.inject.Inject;
+}</pre>
+</div></div><p>Now let’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 <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Named.html"
rel="nofollow">@Named</a> 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;
import javax.inject.Named;
...
@@ -108,19 +161,25 @@ public class AuthenticationFilter implem
...
-}</plain-text-body><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><plain-text-body>public class Car {
+}</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 {
public Car() { ... }
@Inject
public Car(Engine engine) { ... }
-}</plain-text-body><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><plain-text-body>public class Car {
+}</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 {
public Car() { ... }
public Car(Engine engine) { ... }
-}</plain-text-body><p>When several constructors are available and you
don’t want the constructor with most  parameters to be injectable,
you need to place the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a> annotation.</p><plain-text-body>public
class Car {
+}</pre>
+</div></div><p>When several constructors are available and you don’t
want the constructor with most  parameters to be injectable, you need to
place the <a class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a> 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 {
public Car() { ... }
@@ -129,7 +188,9 @@ public class AuthenticationFilter implem
public Car(Engine engine, Logger logger) { ... }
-}</plain-text-body><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 <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a> or <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">@InjectService</a> annotations.
As of version 5.3 the injection points can also be marked with JSR-330
annotations. The following example demonstrates
that.</p><plain-text-body>public class Index {
+}</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 <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Inject.html">@Inject</a> or <a
class="external-link"
href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/InjectService.html">@InjectService</a> 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 {
@Inject
private Request request;
@@ -147,18 +208,24 @@ public class AuthenticationFilter implem
void onActivate() { ... }
-}</plain-text-body><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><plain-text-body>public class Index {
+}</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 {
@Inject
@French
private Greeter greeter;
-}</plain-text-body><p>Again, there is a slight difference. In JSR-330 a
qualifier annotation like <em>@French</em> in the example above needs
to be annotated by the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Qualifier.html"
rel="nofollow">@Qualifier</a> annotation.</p><plain-text-body>@Documented
+}</pre>
+</div></div><p>Again, there is a slight difference. In JSR-330 a qualifier
annotation like <em>@French</em> in the example above needs to be
annotated by the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Qualifier.html"
rel="nofollow">@Qualifier</a> 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
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@javax.inject.Qualifier
public @interface French {
-}</plain-text-body><p>In Tapestry any annotation can be a marker annotation.
You don’t need to place something like the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Qualifier.html"
rel="nofollow">@Qualifier</a> 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><plain-text-body>public class Car {
+}</pre>
+</div></div><p>In Tapestry any annotation can be a marker annotation. You
don’t need to place something like the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Qualifier.html"
rel="nofollow">@Qualifier</a> 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 {
private Engine engine;
@@ -166,7 +233,9 @@ public @interface French {
this.engine = engine;
}
-}</plain-text-body><p>When building a <em>Car</em> instance,
Tapestry IoC will try to resolve a service of type <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 <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Named.html"
rel="nofollow">@Named</a> annotation to inject a service by id into
a <em>contribute method</em>.</p><plain-text-body>public class
TapestryModule {
+}</pre>
+</div></div><p>When building a <em>Car</em> instance, Tapestry IoC
will try to resolve a service of type <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 <a
class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Named.html"
rel="nofollow">@Named</a> annotation to inject a service by id into
a <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 {
@Contribute(BindingSource.class)
public static void provideBindings(
@@ -186,7 +255,8 @@ public @interface French {
}
...
-}</plain-text-body><h1
id="UsingJSR330standardannotations-Scopes">Scopes</h1><p>By default, a JSR-330
injector creates an instance, uses the instance for one injection, and then
forgets it. By placing the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Scope.html"
rel="nofollow">@Scope</a> annotation you can tell the injector to retain
the instance for possible reuse in a later injection. If you want a service to
be a singleton, you need to use the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Singleton.html"
rel="nofollow">@Singleton</a> annotation.</p><p>In Tapestry, it is
exactly the other way around. By default a service is a singleton. Once an
instance is created, it is reused for injection. Another available scope is
<em>perthread</em>, which exists primarily to help multi-threaded servlet
applications. If a service has <em>perthread</em> scope, it is recreate
d for every incoming request.</p><p> </p><p></p></div>
+}</pre>
+</div></div><h1 id="UsingJSR330standardannotations-Scopes">Scopes</h1><p>By
default, a JSR-330 injector creates an instance, uses the instance for one
injection, and then forgets it. By placing the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Scope.html"
rel="nofollow">@Scope</a> annotation you can tell the injector to retain
the instance for possible reuse in a later injection. If you want a service to
be a singleton, you need to use the <a class="external-link"
href="http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Singleton.html"
rel="nofollow">@Singleton</a> annotation.</p><p>In Tapestry, it is
exactly the other way around. By default a service is a singleton. Once an
instance is created, it is reused for injection. Another available scope is
<em>perthread</em>, which exists primarily to help multi-threaded servlet
applications. If a service has <em>perthread</em> scope, it is recreated
for e
very incoming request.</p><p> </p><p></p></div>
</div>
<div class="clearer"></div>