You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by bu...@apache.org on 2013/02/17 17:20:48 UTC

svn commit: r850860 - in /websites/production/tapestry/content: cache/main.pageCache tapestry-for-jsf-users.html

Author: buildbot
Date: Sun Feb 17 16:20:48 2013
New Revision: 850860

Log:
Production update by buildbot for tapestry

Added:
    websites/production/tapestry/content/tapestry-for-jsf-users.html
Modified:
    websites/production/tapestry/content/cache/main.pageCache

Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.

Added: websites/production/tapestry/content/tapestry-for-jsf-users.html
==============================================================================
--- websites/production/tapestry/content/tapestry-for-jsf-users.html (added)
+++ websites/production/tapestry/content/tapestry-for-jsf-users.html Sun Feb 17 16:20:48 2013
@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+	
+	<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<html>
+<head>
+  <meta http-equiv="x-ua-compatible" content="IE=9">
+  <title>
+    Tapestry for JSF Users -- Apache Tapestry
+  </title>
+  <link type="text/css" rel="stylesheet" href="/resources/space.css">
+  <link href="/styles/style.css" rel="stylesheet" type="text/css"/>
+</head>
+<body>
+  <div class="wrapper bs">
+
+<div id="navigation"><div class="nav"><ul class="alternate" type="square"><li><a shape="rect" href="index.html" title="Index">Home</a></li><li><a shape="rect" href="getting-started.html" title="Getting Started">Getting Started</a></li><li><a shape="rect" href="documentation.html" title="Documentation">Documentation</a></li><li><a shape="rect" href="download.html" title="Download">Download</a></li><li><a shape="rect" href="about.html" title="About">About</a></li><li><a shape="rect" href="community.html" title="Community">Community</a></li><li><a shape="rect" class="external-link" href="http://www.apache.org/">Apache</a></li><li><a shape="rect" class="external-link" href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li><li><a shape="rect" class="external-link" href="http://www.apache.org/foundation/thanks.html">Thanks</a></li></ul>
+</div>
+</div>
+
+<div id="top">
+<div id="smallbanner"><div class="searchbox" style="float:right;margin: .3em 1em .1em 1em">
+<span style="color: #999; font-size: 90%">Tapestry docs, issues, wikis &amp; blogs:</span>
+<form enctype="application/x-www-form-urlencoded" method="get" action="http://tapestry.apache.org/search.html">
+  <input type="text" name="q">
+  <input type="submit" value="Search">
+</form>
+
+</div>
+
+<div class="emblem" style="float:left"><a shape="rect" href="index.html" title="Index"><span class="image-wrap" style=""><img src="small-banner.data/tapestry_s.png" style="border: 0px solid black"></span></a></div>
+<div class="title" style="float:left; margin: 0 0 0 3em"><h1><a shape="rect" name="SmallBanner-PageTitle"></a>Tapestry for JSF Users</h1></div></div>
+<div class="clearer"></div>
+</div>
+
+<div class="clearer"></div>
+
+  <div id="breadcrumbs">
+        <a href="index.html">Apache Tapestry</a>&nbsp;&gt;&nbsp;<a href="documentation.html">Documentation</a>&nbsp;&gt;&nbsp;<a href="tapestry-for-jsf-users.html">Tapestry for JSF Users</a>
+    <a class="edit" title="Edit this page (requires approval -- just ask on the mailing list)" href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=30754013">edit</a>
+  </div>
+
+<div id="content">
+<div id="ConfluenceContent"><h1><a shape="rect" name="TapestryforJSFUsers-TapestryforJSFUsers"></a>Tapestry for JSF Users</h1>
+
+<p>This is a guide to help those who already know JavaServer Faces (JSF) learn Apache Tapestry.</p>
+
+<p>JSF is a rich, mature web framework specification, and there are lots of smart people who use it productively. This guide isn't intended as a pro-versus-con comparison or as advocacy any kind. Instead, it just attempts to make it a little easier to transition between the two frameworks, regardless of the reason for doing so.</p>
+
+<p>Almost all modern JSF applications use Facelets as their view technology, so we assume the use of Facelets here for the JSF side.</p>
+
+<h2><a shape="rect" name="TapestryforJSFUsers-SidebysideComparison"></a>Side-by-side Comparison</h2>
+
+<p>JSF and Tapestry have a lot of superficial similarities, so the first steps in that transition are all about relating similar concepts, terms and component names in your mind:</p>
+
+<div class="table-wrap">
+<table class="confluenceTable"><tbody><tr><th colspan="1" rowspan="1" class="confluenceTh"> Concepts &amp; Terminology </th><th colspan="1" rowspan="1" class="confluenceTh"> JSF </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Java class associated with a page or component </td><td colspan="1" rowspan="1" class="confluenceTd"> "Backing Bean" </td><td colspan="1" rowspan="1" class="confluenceTd"> "Component Class" </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Component attributes/parameters </td><td colspan="1" rowspan="1" class="confluenceTd"> "attributes" </td><td colspan="1" rowspan="1" class="confluenceTd"> "parameters" </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> HTML Attribute used for invisible instrumentation </td><td colspan="1" rowspan="1" class="confluenceTd"> jswc="someComponentType" </td><td colspan="1" rowspan="1" class="confluenceTd"> t:type="some
 ComponentType" </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> CSS "class" attribute name </td><td colspan="1" rowspan="1" class="confluenceTd"> styleClass </td><td colspan="1" rowspan="1" class="confluenceTd"> class </td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"> Output and Messages </th><th colspan="1" rowspan="1" class="confluenceTh"> JSF </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Escaped HTML from property </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:outputText value="myBean.myValue"/&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> ${myValue} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Raw HTML from property </td><td colspan="1" rowspan="1" class="confluenceTd"> #{myBean.myValue} </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:outputRaw value="myValue"/&gt;</td></tr><tr><td colspan="1" rowspan="1"
  class="confluenceTd"> Error messages </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:message&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:errors&gt; (for forms) or &lt;t:alerts&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Image display </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:graphicImage&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>use standard &lt;img&gt; tag</em> </td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"> Conditionals and Looping </th><th colspan="1" rowspan="1" class="confluenceTh"> JSF </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Server-side comment </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;ui:remove&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:remove&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Render-time loop </t
 d><td colspan="1" rowspan="1" class="confluenceTd"> &lt;ui:repeat&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:loop&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Compile-time loop </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;c:forEach&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:loop&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Conditional </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;c:if test="#{myBean.myValue}"&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:if test="myValue"&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Conditional </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;ui:fragment rendered="#{myBean.someCondition}"/&gt;...&lt;/ui:fragment&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:if test="someCondition"&gt;...&lt;/t:if&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluen
 ceTd"> Switch </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;c:choose&gt;&lt;c:when ... &gt;&lt;/c:choose&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> See <a shape="rect" href="switching-cases.html" title="Switching Cases">Switching Cases</a> </td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"> Links and Buttons </th><th colspan="1" rowspan="1" class="confluenceTh"> JSF </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Navigational link </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:link outcome="nextpage.xhtml"/&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/PageLink.html">&lt;t:pagelink page="nextpage"/&gt;</a></td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Event-triggering link, without form s
 ubmission </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:actionLink&gt; or &lt;t:eventLink&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Form submission link </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:commandLink&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:linkSubmit&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Form submission button </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:commandButton&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:submit&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Link to Javascript file </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:outputScript&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>&lt;script&gt; or use @Import in component class</em> </td></tr><tr><td colspan="1" rowspan="1" class
 ="confluenceTd"> Link to CSS file </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:outputStylesheet&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>&lt;style&gt; or use @Import in component class</em> </td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"> Grids, Tables and Trees </th><th colspan="1" rowspan="1" class="confluenceTh"> JSF </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Tabular data in &lt;table&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:datatable&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> Grid </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Table used for layout </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:panelGrid&gt; with &lt;h:panelGroup&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>use standard &lt;table&gt; tag</em> </td></tr><tr><td colspan="1" rowspan="
 1" class="confluenceTd"> Hierarchical tree </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>depends on component library</em> </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:tree&gt; </td></tr><tr><th colspan="1" rowspan="1" class="confluenceTh"> Form Tags/Components </th><th colspan="1" rowspan="1" class="confluenceTh"> JSF </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Form </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:form&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:form&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Single-line text input field </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:inputText&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <a shape="rect" class="external-link" href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/TextField.html">
 &lt;t:textField&gt;</a> </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Password field </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:inputSecret&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:password&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Select menu </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:selectOneMenu&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:select&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Checkbox </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:selectBooleanCheckbox&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:checkbox&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Checkbox list </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:selectManyCheckbox&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:checklist&gt; </td></tr><tr><td colspan="1" rowspan
 ="1" class="confluenceTd"> Radio button list </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:selectOneRadio&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:radioGroup&gt; with &lt;t:radio&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Multiple select menu </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:selectManyListbox&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> (but see Palette and Checklist) </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Hidden field </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:inputHidden&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:hidden&gt; </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> textarea tag </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:inputTextarea&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:textArea&gt; </td></tr><tr><td colspan="1" row
 span="1" class="confluenceTd"> Label tag </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;h:outputLabel for="..."&gt; </td><td colspan="1" rowspan="1" class="confluenceTd"> &lt;t:label for="..."&gt; </td></tr></tbody></table>
+</div>
+
+
+<p>Some important notes:</p>
+
+<ul><li>With Tapestry, you don't use the ${...} syntax with parameters of components. Just use a bare expression within the quotes. For example: &lt;t:textfield value="myProperty"&gt; instead of &lt;t:textfield value="${myProperty}"&gt;, because in the latter case the expression is converted to a read-only string before the textfield component gets it.</li></ul>
+
+
+<h2><a shape="rect" name="TapestryforJSFUsers-HelloWorldComparison"></a>Hello World Comparison</h2>
+
+<p>Faces templates and Tapestry templates are superficially quite similar. </p>
+
+<table class="sectionMacro" border="0" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td colspan="1" rowspan="1" valign="top" class="confluenceTd">
+<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;"><b>JSF template (helloworld.xhtml)</b></div><div class="codeContent panelContent">
+<pre class="code-xml">
+&lt;html xmlns=<span class="code-quote">"http://www.w3.org/1999/xhtml"</span>
+        <span class="code-keyword">xmlns:h</span>=<span class="code-quote">"http://java.sun.com/jsf/html"</span>
+        <span class="code-keyword">xmlns:f</span>=<span class="code-quote">"http://java.sun.com/jsf/core"</span>&gt;
+  <span class="code-tag">&lt;h:body&gt;</span>
+    <span class="code-tag">&lt;p&gt;</span><span class="code-tag">&lt;h:outputText value="#{helloWorldBean.greeting} /&gt;</span><span class="code-tag">&lt;/p&gt;</span>
+  <span class="code-tag">&lt;/h:body&gt;</span>
+<span class="code-tag">&lt;/html&gt;</span>
+</pre>
+</div></div></td><td colspan="1" rowspan="1" valign="top" class="confluenceTd">
+<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;"><b>Tapestry template (HelloWorld.tml)</b></div><div class="codeContent panelContent">
+<pre class="code-xml">
+<span class="code-tag">&lt;html <span class="code-keyword">xmlns:t</span>=<span class="code-quote">"http://tapestry.apache.org/schema/tapestry_5_3.xsd"</span>&gt;</span>
+  <span class="code-tag">&lt;body&gt;</span>
+    <span class="code-tag">&lt;p&gt;</span>${greeting}<span class="code-tag">&lt;/p&gt;</span>
+  <span class="code-tag">&lt;/body&gt;</span>
+<span class="code-tag">&lt;/html&gt;</span>
+</pre>
+</div></div></td></tr></tbody></table>
+
+<p>Though these are very similar, notice some differences:</p>
+<ul><li>The #{...} syntax in JSF does not encode the underlying string, so you have to use the &lt;h:outputText&gt; tag if your data may contain HTML reserved characters such as &lt;, &gt;, or &amp;. In contrast, the ${...} syntax in Tapestry <b>does</b> encode the underlying string.</li><li>In JSF, backing beans are not necessarily related one-to-one with page templates. Often several templates use the same backing bean, and one template may reference multiple backing beans. In Tapestry, they are always related one-to-one, and therefore you don't have to specify which component class your ${...} expressions are referencing.</li></ul>
+
+
+<table class="sectionMacro" border="0" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td colspan="1" rowspan="1" valign="top" class="confluenceTd">
+<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;"><b>JSF Backing Bean (HelloWorldBean.java)</b></div><div class="codeContent panelContent">
+<pre class="code-java">
+@ManagedBean
+@RequestScoped
+<span class="code-keyword">public</span> class HelloWorldBean {
+    <span class="code-keyword">public</span> <span class="code-object">String</span> getGreeting() {
+        <span class="code-keyword">return</span> <span class="code-quote">"Hello, World!"</span>;
+    }
+}
+</pre>
+</div></div></td><td colspan="1" rowspan="1" valign="top" class="confluenceTd">
+<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;"><b>Tapestry page class (HelloWorld.java)</b></div><div class="codeContent panelContent">
+<pre class="code-java">
+<span class="code-keyword">public</span> class HelloWorld {
+    <span class="code-keyword">public</span> <span class="code-object">String</span> getGreeting() {
+        <span class="code-keyword">return</span> <span class="code-quote">"Hello, World!"</span>;
+    }
+}
+</pre>
+</div></div></td></tr></tbody></table>
+
+<h2><a shape="rect" name="TapestryforJSFUsers-Expressionsintemplates"></a>Expressions in templates</h2>
+
+<p>JSF uses the Unified Expression Language with the #{...} or ${...} syntax for accessing Backing Bean properties. For its part, Tapestry uses the ${...} syntax with a similar but more limited expression language called <a shape="rect" href="property-expressions.html" title="Property Expressions">Property Expressions</a>. Both allow easy access to properties via the usual JavaBean conventions, but with Tapestry you don't have to specify which class the expression starts at (because it always starts at the component class corresponding to the template). Some comparisons:</p>
+
+<div class="table-wrap">
+<table class="confluenceTable"><tbody><tr><td colspan="1" rowspan="1" class="confluenceTd">&#160;</td><th colspan="1" rowspan="1" class="confluenceTh"> JSF Syntax </th><th colspan="1" rowspan="1" class="confluenceTh"> Tapestry Syntax </th></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Property (calls getMyProperty() or setMyProperty()) </td><td colspan="1" rowspan="1" class="confluenceTd"> #{employeeBean.employeeName} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${employeeName} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Boolean property (calls isHourly() or setHourly()) </td><td colspan="1" rowspan="1" class="confluenceTd"> #{employeeBean.hourly} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${hourly} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Property chain </td><td colspan="1" rowspan="1" class="confluenceTd"> #{employeeBean.address.street} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${addr
 ess.street} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Null-safe property chain </td><td colspan="1" rowspan="1" class="confluenceTd"> #{employeeBean.address.street} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${address?.street} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> 5th element in a List </td><td colspan="1" rowspan="1" class="confluenceTd"> #{employeeBean.employees[5].name} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${employees.get(5).name} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Negation </td><td colspan="1" rowspan="1" class="confluenceTd"> #{! employeeBean.hourly} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${! hourly} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Arithmetic &amp; relational operators </td><td colspan="1" rowspan="1" class="confluenceTd"> +-*/% div mod </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> </td
 ></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Relational operators </td><td colspan="1" rowspan="1" class="confluenceTd"> == != ne &lt; lt &gt; gt &lt;= le &gt;= ge </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Ternary operator </td><td colspan="1" rowspan="1" class="confluenceTd"> #{myBean.foo &lt; 0 ? 'bar' : 'baz'} </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Method calling </td><td colspan="1" rowspan="1" class="confluenceTd"> #{myBean.employees.size()} </td><td colspan="1" rowspan="1" class="confluenceTd"> ${employees.size()} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Iterated Range </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not avaialble</em> </td><td colspan="1" rowspan="1" class="confluenceTd"> ${1..10} </td></tr><tr><td colspa
 n="1" rowspan="1" class="confluenceTd"> Iterated Range (calculated) </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not avaialble</em> </td><td colspan="1" rowspan="1" class="confluenceTd"> ${1..groupList.size()} </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> List </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> </td><td colspan="1" rowspan="1" class="confluenceTd"> ${ [ user.name, user.email, user.phone ] } </td></tr><tr><td colspan="1" rowspan="1" class="confluenceTd"> Map </td><td colspan="1" rowspan="1" class="confluenceTd"> <em>not available</em> </td><td colspan="1" rowspan="1" class="confluenceTd"> ${ { 'id':'4039','type':'hourly' } } </td></tr></tbody></table>
+</div>
+
+
+<p>Features shown as <em>not available</em> above are absent by design, because (in both Tapestry and JSF) it is considered best to keep complex logic in the component class rather than in the template.</p>
+
+<h2><a shape="rect" name="TapestryforJSFUsers-EventHandling%26PageNavigation"></a>Event Handling &amp; Page Navigation</h2>
+
+<h3><a shape="rect" name="TapestryforJSFUsers-Eventhandling"></a>Event handling</h3>
+
+<p>In JSF, you specify the event via the <tt>action</tt> parameter (for example, &lt;h:commandButton value="Submit" action="employeeBean.saveChanges"&gt;). For Tapestry, event handler methods are found by method naming conventions (on_EventName_() or by method annotations (@Event), based on a combination of the "t:id" attribute and event name, and the action name used depends on the component. For example, the "&lt;actionlink&gt;" component in Tapestry emits an "action" event when clicked, and you handle that event in your "onAction()" method.</p>
+
+<h2><a shape="rect" name="TapestryforJSFUsers-Validation"></a>Validation</h2>
+
+<p>Tapestry applications can use JSR 303 Bean Validation annotations that JSF users should be familiar with:</p>
+
+<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
+<pre class="code-java">
+<span class="code-keyword">public</span> class Employee {
+    @Validate(<span class="code-quote">"required,minlength=2,maxlength=100"</span>)
+    <span class="code-keyword">private</span> <span class="code-object">String</span> lastName;
+    @NotNull @Email <span class="code-keyword">private</span> <span class="code-object">String</span> email;
+</pre>
+</div></div>
+
+<h3><a shape="rect" name="TapestryforJSFUsers-PostRedirectGetNavigation"></a>Post-Redirect-Get Navigation</h3>
+
+<p>By default, most JSF URLs are "one page behind". That is, when you click on an &lt;h:commandLink&gt; link or submit a form, the request goes back to the originating page, and the server returns the contents of the <b>next</b> page &#8211; but the URL in the browser shows the previous page's URL. To fix this in JSF you add the "?faces-redirect=true" to the URL you return from event handlers, which causes JSF to send a redirect to the browser to navigate to the next page.</p>
+
+<p>By contrast, Tapestry implements this Post-Redirect-Get pattern by default. The URL will always reflect the page you're seeing, not the page you just came from.</p>
+
+<p>Note that by default Tapestry does not save property values across the Post-Redirect-Get cycle. This means that you have to consider how (and whether) to persist property values from one page to the next.  The usual solution is to either make the values part of the page's <a shape="rect" href="navigation.html#Navigation-PageActivation">Activation Context</a> or <a shape="rect" href="persistent-page-data.html" title="Persistent Page Data">@Persist the properties</a> in the session.</p>
+
+<h2><a shape="rect" name="TapestryforJSFUsers-CustomandCompositeComponents"></a>Custom and Composite Components</h2>
+
+<p>With JSF, creating custom components is an <a shape="rect" class="external-link" href="http://jsfcorner.blogspot.com/2011/01/custom-components.html" >advanced topic</a>. In fact, many JSF developers have <em>never</em> created a custom component. In JSF 1.x, creating each custom component requires a lot of work: creating 3 Java classes (component, component renderer and component tag), registering the component in an XML file, and registering the tag in the .tld file. In JSF 2.x <em>composite components</em> can be created without too much work (if your needs can be met by combining existing components <em>and</em> you don't need any custom Java), but you still have to use cumbersome &lt;composite:interface&gt; and &lt;composite:implementation&gt; tags in your component templates, and you have to list the composite components in the xml namespace declaration at the top of the pages where you are using them.</p>
+
+<p>Creating true custom components in JSF 2.0 still requires several steps: create a component class (generally having the @FacesComponent annotation and extending UIComponentBase), create a renderer class (generally extending Renderer), add a &lt;renderer&gt; section to the facesconfig file, and create a *-taglib.xml file in the WEB_INF folder that defines the namespace, tag and component type of the custom component.</p>
+
+<p>In contrast, with Tapestry, <a shape="rect" href="component-classes.html" title="Component Classes">creating custom components</a> is a <em>beginner</em> topic: it is expected to be a daily activity for developers, because it is so easy. In fact, the steps are the same as creating a page. All you have to do is create a (potentially empty) Java class in a "components" sub-package, and create a template file containing (X)HTML markup in the corresponding "components" sub-folder within your package hierarchy under /src/main/resources. You <em>use</em> a custom component just like you use any built-in Tapestry component: <tt>&lt;t:mycomponent&gt;</tt>.</p>
+
+<p>Because they're so easy to create, Tapestry applications tend to have a lot of custom components and much less repetition of HTML than most JSF applications.</p>
+
+<h2><a shape="rect" name="TapestryforJSFUsers-OtherReferences"></a>Other References</h2>
+
+<ul><li><a shape="rect" class="external-link" href="http://blog.tapestry5.de/wp-content/uploads/2010/06/JSF-2.0-vs-Tapestry-5.pdf" >JavaServer Faces 2.0 vs. Tapestry 5: A Head-to-Head Comparison</a> slides by Igor Drobiazko, June 2010.</li><li><a shape="rect" class="external-link" href="http://docs.oracle.com/javaee/6/tutorial/doc/gkhxa.html" >Composite Components: Advanced Topics and Example</a> part of <em>The Java EE 6 Tutorial</em> from Oracle</li></ul>
+</div>
+</div>
+
+<div class="clearer"></div>
+<div id="footer">
+<div id="footer"><p>Apache Tapestry, Tapestry, Apache, the Apache feather logo, and the Apache Tapestry project logo are trademarks of The Apache Software Foundation.</p>
+<script type="text/javascript">
+  var _gaq = _gaq || [];
+  _gaq.push(['_setAccount', 'UA-400821-1']);
+  _gaq.push(['_trackPageview']);
+
+  (function() {
+    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+  })();
+</script></div>
+</div>
+  </div>
+</body>
+</html>