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 121 changed 3 lines. |
public void testGetPeople() { |
List results = mgr.getPeople(new Person()); |
assertTrue(results.size() > 0); |
public void testGetPeople() throws Exception { |
List results = new ArrayList(); |
person = new Person(); |
results.add(person); |
|
// set expected behavior on dao |
personDao.expects(once()).method("getPeople") |
.will(returnValue(results)); |
|
List people = personManager.getPeople(null); |
assertTrue(people.size() == 1); |
personDao.verify(); |
At line 187 changed 1 line. |
To begin, create test/web/**/action/PersonListTest.java (extends BasePage implements PageRenderListener) and add two methods. The first, {{pageBeginRender()}} is executed when the page first loads. The second, {{edit()}} is a listener method that's invoked when you click to edit a row. This is pretty much the same as the {{edit()}} method on PersonForm. The one on PersonForm was mainly added for demonstration purposes. AppGen will won't generate an "edit" listener on the form class if you use it to generate your page classes. |
To begin, create test/web/**/action/PersonListTest.java and add test methods for editing and searching. The {{edit()}} method is a listener method that's invoked when you click to edit a row. This is pretty much the same as the {{edit()}} method on PersonForm. The one on PersonForm was mainly added for demonstration purposes. AppGen will won't generate an "edit" listener on the form class if you use it to generate your page classes. |
At line 193 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 199 changed 4 lines. |
protected void setUp() throws Exception { |
super.setUp(); |
page = (PersonList) getPage(PersonList.class); |
|
protected void onSetUpBeforeTransaction() throws Exception { |
super.onSetUpBeforeTransaction(); |
At line 204 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 208 changed 2 lines. |
protected void tearDown() throws Exception { |
super.tearDown(); |
protected void onTearDownAfterTransaction() throws Exception { |
super.onTearDownAfterTransaction(); |
At line 214 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 218 removed 1 line. |
|
At line 223 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 232 changed 2 lines. |
!!Create PersonController [#6] |
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 PersonList [#6] |
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 242 removed 2 lines. |
import org.apache.tapestry.event.PageEvent; |
import org.apache.tapestry.event.PageRenderListener; |
At line 248 changed 1 line. |
public abstract class PersonList extends BasePage implements PageRenderListener { |
public abstract class PersonList extends BasePage { |
At line 250 removed 3 lines. |
public abstract void setPersonManager(PersonManager manager); |
public abstract List getPeople(); |
public abstract void setPeople(List people); |
At line 254 changed 2 lines. |
public void pageBeginRender(PageEvent event) { |
setPeople(getPersonManager().getPeople(null)); |
public List getPersons() { |
return getPersonManager().getPeople(); |
At line 257 changed 1 line. |
|
|
At line 259 changed 2 lines. |
Object[] parameters = cycle.getServiceParameters(); |
Long id = (Long) parameters[0]; |
Object[] parameters = cycle.getListenerParameters(); |
Long personId = (Long) parameters[0]; |
At line 263 changed 1 line. |
log.debug("fetching person with id: " + id); |
log.debug("fetching person with personId: " + personId); |
At line 266 changed 2 lines. |
Person person = getPersonManager().getPerson(id.toString()); |
|
Person person = getPersonManager().getPerson(personId); |
|
At line 288 removed 4 lines. |
Change it in personList.page too: |
|
{{{<property-specification name="people" type="java.util.List"/>}}} |
|
At line 298 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 308 changed 1 line. |
In the last tutorial, the Canoo tests verify that the user sees the Main Menu after saving/editing a user. Since you've changed that logic, and now you're activating the PersonList, you need to update the tests. Open test/web/web-tests.xml and change any "mainMenu" references in the "PersonsTests" to be go to the people list. |
In the last tutorial, the Canoo tests verify that the user sees the Main Menu after saving/editing a person. Since you've changed that logic, and now you're activating the PersonList, you need to update the tests. Open test/web/web-tests.xml and change any "mainMenu" references in the "PersonsTests" to be go to the people list. |
At line 310 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 314 changed 1 line. |
{{{<verifytitle description="we should see the personDetail title" text=".*${personDetail.title}.*" regex="true"/>}}} |
{{{{{{<verifyTitle description="Person List appears if save successful" text=".*${personList.title}.*" regex="true"/>}}}}}} |
At line 371 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 380 changed 1 line. |
<Menu name="PeopleMenu" title="menu.viewPeople" forward="viewPeople"/> |
<Menu name="PeopleMenu" title="menu.viewPeople" page="/people.html"/> |
At line 407 changed 1 line. |
Notice that there is a new link on the left side (from mainMenu.jsp) and on the right in our menu (from menu.jsp). |
Notice that there is a new link on the left side (from mainMenu.html) and on the right in our menu (from menu.jsp). |