| At line 21 changed 1 line. | 
| Implementing validation with Tapestry is quite simple.  Rather than using a [TextField|http://jakarta.apache.org/tapestry/doc/ComponentReference/TextField.html] component, you use a [ValidField|http://jakarta.apache.org/tapestry/doc/ComponentReference/ValidField.html] components.  The validation integration for the personForm.html page is already done by AppGen in the personForm.page.  If you open web/pages/personForm.page, you should see the following XML.  I've highlighted the portions that are relevant to validation. | 
| Implementing validation with Tapestry is quite simple.  All you need to do is configure a "validators" property on your [TextField|http://jakarta.apache.org/tapestry/doc/ComponentReference/TextField.html] components. The validation integration for the personForm.html page is already done by AppGen in the personForm.page.  If you open web/pages/personForm.page, you should see the following XML. | 
| At line 23 changed 33 lines. | 
| <pre> | 
| <?xml version="1.0"?> | 
| <!DOCTYPE page-specification PUBLIC | 
| "-//Apache Software Foundation//Tapestry Specification 3.0//EN" | 
| "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd"> | 
|  | 
| <page-specification class="org.appfuse.webapp.action.PersonForm"> | 
| <span style="background-color: yellow"><bean name="delegate" class="org.appfuse.webapp.action.Validator"/></span> | 
|  | 
| <span style="background-color: yellow"><bean name="requiredValidator" class="org.apache.tapestry.valid.StringValidator"></span> | 
| <span style="background-color: yellow"><set-property name="required" expression="true"/></span> | 
| <span style="background-color: yellow"><set-property name="clientScriptingEnabled" expression="true"/></span> | 
| <span style="background-color: yellow"></bean></span> | 
|  | 
| <property-specification name="person"  type="org.appfuse.model.Person"/> | 
| <property-specification name="manager" type="org.appfuse.service.Manager"> | 
| global.appContext.getBean("manager") | 
| </property-specification> | 
| <property-specification name="message" type="java.lang.String"/> | 
|  | 
| <span style="background-color: yellow"><component id="firstNameField" type="ValidField"></span> | 
| <span style="background-color: yellow"><binding name="value" expression="person.firstName"/></span> | 
| <span style="background-color: yellow"><binding name="validator" expression="beans.requiredValidator"/></span> | 
| <span style="background-color: yellow"><message-binding name="displayName" key="person.firstName"/></span> | 
| <span style="background-color: yellow"></component></span> | 
|  | 
| <span style="background-color: yellow"><component id="lastNameField" type="ValidField"></span> | 
| <span style="background-color: yellow"><binding name="value" expression="person.lastName"/></span> | 
| <span style="background-color: yellow"><binding name="validator" expression="beans.requiredValidator"/></span> | 
| <span style="background-color: yellow"><message-binding name="displayName" key="person.lastName"/></span> | 
| <span style="background-color: yellow"></component></span> | 
| </page-specification> | 
| </pre> | 
| {{{<?xml version="1.0" encoding="UTF-8"?> | 
| <!DOCTYPE page-specification PUBLIC | 
| "-//Apache Software Foundation//Tapestry Specification 4.0//EN" | 
| "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd"> | 
| At line 28 added 27 lines. | 
| <page-specification class="org.appfuse.webapp.action.PersonForm"> | 
| <inject property="engineService" object="engine-service:page"/> | 
| <inject property="request" object="service:tapestry.globals.HttpServletRequest"/> | 
| <inject property="response" object="service:tapestry.globals.HttpServletResponse"/> | 
| <inject property="personManager" type="spring" object="personManager"/> | 
|  | 
| <bean name="delegate" class="org.appfuse.webapp.action.Validator"/> | 
| <property name="message" persist="flash"/> | 
|  | 
| <component id="personForm" type="Form"> | 
| <binding name="delegate" value="ognl:beans.delegate"/> | 
| <binding name="clientValidationEnabled" value="true"/> | 
| </component> | 
|  | 
| <component id="firstNameField" type="TextField"> | 
| <binding name="value" value="person.firstName"/> | 
| <binding name="validators" value="validators:required"/> | 
| <binding name="displayName" value="message:person.firstName"/> | 
| </component> | 
| <component id="lastNameField" type="TextField"> | 
| <binding name="value" value="person.lastName"/> | 
| <binding name="validators" value="validators:required"/> | 
| <binding name="displayName" value="message:person.lastName"/> | 
| </component> | 
|  | 
| </page-specification>}}} | 
|  | 
| At line 59 changed 1 line. | 
| There are a [number of different validators|http://jakarta.apache.org/tapestry/doc/ComponentReference/ValidField.html] you can use, this example just shows a way to validate Strings are entered.  The userForm.page has examples of an EmailValidator and a PatternValidator. All input fields generated by AppGen are required by default.  You can change this by modifying the extras/appgen/src/**/*_page.xdt files. | 
| There are a [number of different validators|http://jakarta.apache.org/tapestry/UsersGuide/validation.html] you can use, this example just shows a way to validate Strings are entered.  The userForm.page has examples of an EmailValidator and a PatternValidator. All input fields generated by AppGen are required by default.  You can change this by modifying the extras/appgen/src/**/*_page.xdt files. | 
| At line 65 changed 1 line. | 
| if (getValidationDelegate().getHasErrors()) { | 
| if (getDelegate().getHasErrors()) { | 
| At line 71 changed 1 line. | 
| Since validation is configured in the page-specification file (a.k.a. personForm.page), whenever a field uses the ValidField component, it will get validated.  For those fields that don't have validation, make sure and use the @Label component for its label.  Below is a non-validated field example from web/pages/userForm.html. | 
| Since validation is configured in the page-specification file (a.k.a. personForm.page), whenever a field specifies a "validators" property.  For those fields that don't have validation, make sure and use the @Label component for its label.  Below is a non-validated field example from web/pages/userForm.html. | 
| At line 202 changed 1 line. | 
| import org.appfuse.model.Person; | 
| import org.apache.tapestry.engine.RequestCycle; | 
| At line 202 added 1 line. | 
| import org.appfuse.model.Person; | 
| At line 204 added 3 lines. | 
| import java.util.HashMap; | 
| import java.util.Map; | 
|  | 
| At line 208 changed 4 lines. | 
| protected void setUp() throws Exception { | 
| super.setUp(); | 
| page = (PersonList) getPage(PersonList.class); | 
|  | 
| protected void onSetUpBeforeTransaction() throws Exception { | 
| super.onSetUpBeforeTransaction(); | 
| At line 213 changed 2 lines. | 
| page.setManager((Manager) ctx.getBean("manager")); | 
| page.setRequestCycle(getCycle(request, response)); | 
| Map map = new HashMap(); | 
| map.put("personManager", (Manager) applicationContext.getBean("personManager")); | 
| page = (PersonList) getPage(PersonList.class, map); | 
| At line 217 changed 2 lines. | 
| protected void tearDown() throws Exception { | 
| super.tearDown(); | 
| protected void onTearDownAfterTransaction() throws Exception { | 
| super.onTearDownAfterTransaction(); | 
| At line 223 changed 3 lines. | 
| MockRequestCycle cycle = (MockRequestCycle) page.getRequestCycle(); | 
| cycle.addServiceParameter(new Long(1)); | 
|  | 
| RequestCycle cycle = new MockRequestCycle(); | 
| cycle.setServiceParameters(new Object[] {new Long("1")}); | 
| At line 227 removed 1 line. | 
|  | 
| At line 232 changed 3 lines. | 
| PageEvent event = new PageEvent(page, page.getRequestCycle()); | 
| page.pageBeginRender(event); | 
| assertTrue(page.getPeople().size() >= 1); | 
| assertTrue(page.getPersons().size() >= 1); | 
| At line 242 changed 1 line. | 
| Create src/web/**/action/PersonList.java.  It should implement [PageRenderListener|http://jakarta.apache.org/tapestry/doc/api/org/apache/tapestry/event/PageRenderListener.html] and read as follows: | 
| Create src/web/**/action/PersonList.java.  It should implement [PageBeginRenderListener|http://jakarta.apache.org/tapestry/tapestry/apidocs/org/apache/tapestry/event/PageBeginRenderListener.html] and read as follows: | 
| At line 251 removed 2 lines. | 
| import org.apache.tapestry.event.PageEvent; | 
| import org.apache.tapestry.event.PageRenderListener; | 
| At line 257 changed 1 line. | 
| public abstract class PersonList extends BasePage implements PageRenderListener { | 
| public abstract class PersonList extends BasePage { | 
| At line 259 removed 3 lines. | 
| public abstract void setPersonManager(PersonManager manager); | 
| public abstract List getPeople(); | 
| public abstract void setPeople(List people); | 
| At line 263 changed 2 lines. | 
| public void pageBeginRender(PageEvent event) { | 
| setPeople(getPersonManager().getPeople(null)); | 
| public List getPersons() { | 
| return getPersonManager().getPeople(); | 
| At line 266 changed 1 line. | 
|  | 
|  | 
| At line 268 changed 2 lines. | 
| Object[] parameters = cycle.getServiceParameters(); | 
| Long id = (Long) parameters[0]; | 
| Object[] parameters = cycle.getListenerParameters(); | 
| Long personId = (Long) parameters[0]; | 
| At line 272 changed 1 line. | 
| log.debug("fetching person with id: " + id); | 
| log.debug("fetching person with personId: " + personId); | 
| At line 275 changed 2 lines. | 
| Person person = getPersonManager().getPerson(id.toString()); | 
|  | 
| Person person = getPersonManager().getPerson(personId); | 
|  | 
| At line 297 removed 4 lines. | 
| Change it in personList.page too: | 
|  | 
| {{{<property-specification name="people" type="java.util.List"/>}}} | 
|  | 
| At line 307 changed 1 line. | 
| You should already have the title and heading keys (personList.title and personList.heading) for the personList.html page in web/WEB-INF/classes/ApplicationResources_en.properties.  You should've added these keys in the previous tutorial. The column names automatically resolve to i18n messages based on their identifier (with the . in them). | 
| You should already have the title and heading keys (personList.title and personList.heading) for the personList.html page in web/WEB-INF/classes/ApplicationResources.properties.  You should've added these keys in the previous tutorial. The column names automatically resolve to i18n messages based on their identifier (with the . in them). | 
| At line 319 changed 1 line. | 
| {{{<verifytitle description="display Main Menu" text=".*${mainMenu.title}.*" regex="true"/>}}} | 
| {{{<verifyTitle description="display Main Menu" text=".*${mainMenu.title}.*" regex="true"/>}}} | 
| At line 323 changed 1 line. | 
| {{{{{{<verifytitle description="Person List appears if save successful" text=".*${personList.title}.*" regex="true"/>}}}}}} | 
| {{{{{{<verifyTitle description="Person List appears if save successful" text=".*${personList.title}.*" regex="true"/>}}}}}} | 
| At line 380 changed 1 line. | 
| Where ''menu.viewPeople'' is an entry in web/WEB-INF/classes/ApplicationResources_en.properties. | 
| Where ''menu.viewPeople'' is an entry in web/WEB-INF/classes/ApplicationResources.properties. | 
| At line 389 changed 1 line. | 
| <Menu name="PeopleMenu" title="menu.viewPeople" forward="viewPeople"/> | 
| <Menu name="PeopleMenu" title="menu.viewPeople" page="/people.html"/> |