表单输入与验证
表单输入是任何应用的生命之血;这是一种从用户收集有用信息的最有效的方式。不管它是一个搜索表单、一个登录还是一个多页的注册向导,用户借助表单在应用中真正地表达他们。
Tapestry 5输入验证在创建和验证输入有很好的表现。输入验证是声明式的,意味首我们简单地告诉Tapestry一个表单域应用哪种验证,然后Tapestry会在服务器端(已实现)与客户端维护这种验证。
最后,Tapestry不仅能够将错误的信息表现给用户,而且还能对表单域及其标注(labels)进行装饰,标记它们包含错误(主要利用CSS效果)。
表单组件
Tapestry的表单支持的核心即是表单组件,表单组件封装着其他所有表单域组件,如TextField、TextArea、Checkbox等等。
表单组件产生许多组件事件,我们可以给其提供事件处理方法。
呈现时,表单组件发出一个“prepare”通知,以使表单容器创建将要在表单中引用的表单域或属性。如,这是一个创建被呈现的临时实体对像或者载入一个源自数据库的可被编辑的实体的好机会。
在Tapestry 5输入验证中,当用户在客户端提交表单时,服务器端会执行一系列的步骤。
首先,表单呈现时会发出一个“prepare”通知。
其次,所有的表单域被激活并将从请求中得到相应的值,验证它们及(如果有效)保存现有的变化。
对Tapestry 4的用户:Tapestry 5不使用Tapestry 4中脆弱的表单刷新(form rewind)方法,而是在表单呈现时产生一个存放是否需要处理表单提交信息的隐藏域。
表单域流程处理完后,表单发出一个“validate”事件,这是一个执行跨表单验证(还不能公布其详情)的机会。
然后,表单确定是否存在任何Tapestry 5输入验证错误。如果存在,表单提交失败并发出一个"failure"事件。如果没有验证错误,些时将发出一个"success"事件。
最后,表单发出一个"submit"事件(逻辑上,它不考虑成功与否)。
跟踪验证错误
一个与表单关联的就是验证跟踪器(ValidationTracker),它跟踪着表单域对应的所有的用户输入与用户验证错误。跟踪器可以通过跟踪器参数提供给表单,不过很少用到。
表单(Form)包括两个isValid() 和 getHasErrors()方法,用来查看表单验证跟踪器是否存在任何错误。
在我们的逻辑中,我们可以记录验证错误。表单(Form)包括两个不同版本的recordError()方法,一个是指定一个表单域(Field,一个被所有表单元素组件实现的接口),另外一个是全局验证错误("global" errors),与具体的表单域无关。
在请求间保存数据
因为其他的动作请求,表单提交的结果会向客户端发出一个重定向来重新呈现页面。验证跟踪器必须在请求间被持久化地(persistently)保存下来,否则所有的Tapestry 5输入验证信息会丢失(表单提供一个persisten形式的默认验证跟踪器)。
同样地,组件更新单独的表单域也应该被持久化。
比如,一个用来收集用户名与密码的登录页面,应该如下:
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;
}
}
因为Form表单提交实际上是两个请求(提交自己,然后重新呈现页面),所以需要在两个请求间持久化保存在_userName属性里的值。属性_password同样需要,除非PasswordField组件从不呈现值。
注意onSuccess()方法不是公共的(public);事件处理方法可以具有任何的可见性,甚至私有的。包可见性(即无可见性修饰)比较常用,这时它允许组件可被相同包下的测试用例类测试。
假如Form先前没有存在验证错误,它仅产生一个"success"事件,这意味着没有必要在方法的第一行写上if (_form.getHasErrors()) return;这样的语句。
最后,注意业务逻辑如何与表单验证相关联。UserAuthenticator服务用来保证userName 和 (文本的) password的有效性。当它返回false时,我们用Form组件来记录一个错误。我们提供一个PasswordField实例作为它的第一个参数,这保证了密码表单域和它的标注(label)会在Form表单重新呈现时被修饰以表现错误给用户看。