因为其他的动作请求,表单提交的结果会向客户端发出一个重定向来重新呈现页面。验证跟踪器必须在请求间被持久化地(persistently)保存下来,否则所有的验证信息会丢失(表单提供一个persisten形式的默认验证跟踪器)。
同样地,组件更新单独的表单域也应该被持久化。
比如,一个用来收集用户名与密码的登录页面,应该如下:
因为Form表单提交实际上是两个请求(提交自己,然后重新呈现页面),所以需要在两个请求间持久化保存在_userName属性里的值。属性_password同样需要,除非PasswordField组件从不呈现值。public class Login
...{
@Persist
private String _userName;
![]()
private String _password;
![]()
@Inject
private UserAuthenticator _authenticator;
![]()
@Component(id = "password")
private PasswordField _passwordField;
![]()
@Component
private Form _form;
![]()
String onSuccess()
...{
if (!_authenticator.isValid(_userName, _password))
...{
_form.recordError(_passwordField, "Invalid user name or password.");
return null;
}
![]()
return "PostLogin";
}
![]()
public String getPassword()
...{
return _password;
}
![]()
public void setPassword(String password)
...{
_password = password;
}
![]()
public String getUserName()
...{
return _userName;
}
![]()
public void setUserName(String userName)
...{
_userName = userName;
}
}
注意onSuccess()方法不是公共的(public);事件处理方法可以具有任何的可见性,甚至私有的。包可见性(即无可见性修饰)比较常用,这时它允许组件可被相同包下的测试用例类测试。
假如Form先前没有存在验证错误,它仅产生一个"success"事件,这意味着没有必要在方法的第一行写上if (_form.getHasErrors()) return;这样的语句。
最后,注意业务逻辑如何与表单验证相关联。UserAuthenticator服务用来保证userName 和 (文本的) password的有效性。当它返回false时,我们用Form组件来记录一个错误。我们提供一个PasswordField实例作为它的第一个参数,这保证了密码表单域和它的标注(label)会在Form表单重新呈现时被修饰以表现错误给用户看。
配置表单域及标注
页面模板包含少量的Tapestry 标识(instrumentation):
Tapestry的Form组件负责创建form表单提交所需的URL(这个Tapestry的责任,不是你的)。<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head>
<title>Login</title>
</head>
<body>
<h1>Please Login</h1>
![]()
<t:form>
![]()
<t:errors/>
![]()
<t:label for="userName"/>:
<input t:type="TextField" t:id="userName" t:validate="required,minlength=3" size="30"/>
<br/>
<t:label for="password"/>:
<input t:type="PasswordField" t:id="password" t:validate="required,minlength=3" size="30"/>
<br/>
<input type="submit" value="Login"/>
</t:form>
</body>
</html>
Errors组件必须放在Form里,它将Form组件里表单域的所有错误信息作为一个列表输出,并应用一此简单的样式使得结果更显眼。
每一个表单域组件,比如TextField,与Label组件成对。Label将呈现一个与表单域相连的<label>元素。这个组件对方便使用非常重要,特别对那些有视觉障碍(残疾)的用户。它意味着你可以能过点击标注(label)文本将光标移动到相应的表单域中。
Label组件的for参数即是一组件的id。
对于TextField,我们提供了一个组件id为userName。我们可以指定一个value参数,默认情况下这个value参数是匹配TextField的id,TextField的id又对应于组件容器(Login页)的一个属性(假如这个属性存在)。
根据经验,你通常应该为表单域指定一个特定的id(这个id将会被用来呈现标签的name与id属性)。允许省略value参数有利于防止模板变得更加混乱。
用来验证表单域的validate参数标识,是一个验证器的名字列表。验证器在Tapestry中被配置,可用的验证器都是可扩展的。"required"是一个内置验证器的名字,用以保证提交的值不为空串,此外,"minlen"用来保证值具有最小的指定长度。
Validate参数用t:前缀被放置在Tapestry的命名空间里。这不是严格需要的,只是让模板有个良好的格式。然而,在Tapestry命名空间放置Tapestry特定的值保证了模板的有效性。
