表单输入与验证
表单输入是任何应用的生命之血;这是一种从用户收集有用信息的最有效的方式。不管它是一个搜索表单、一个登录还是一个多页的注册向导,用户借助表单在应用中真正地表达他们。
Tapestry
在创建表单和验证输入有很好的表现。输入验证是声明式的,意味首我们简单地告诉 Tapestry
一个表单域应用哪种验证,然后 Tapestry
会在服务器端(已实现)与客户端维护这种验证。
最后, Tapestry
不仅能够将错误的信息表现给用户,而且还能对表单域及其标注(
labels
)进行装饰,标记它们包含错误(主要利用 CSS
效果)。
表单组件
Tapestry
的表单支持的核心即是表单组件,表单组件封装着其他所有表单域组件,如 TextField
、 TextArea
、 Checkbox
等等。
表单组件产生许多组件事件,我们可以给其提供事件处理方法。
呈现时,表单组件发出一个“
prepare
”通知,以使表单容器创建将要在表单中引用的表单域或属性。如,这是一个创建被呈现的临时实体对像或者载入一个源自数据库的可被编辑的实体的好机会。
当用户在客户端提交表单时,服务器端会执行一系列的步骤。
首先,表单呈现时会发出一个“
prepare
”通知。
其次,所有的表单域被激活并将从请求中得到相应的值,验证它们及(如果有效)保存现有的变化。
对 Tapestry 4
的用户: Tapestry 5
不使用 Tapestry 4
中
脆弱的表单刷新(
form rewind
)方法,而是在表单呈现时产生一个存放是否需要处理表单提交信息的隐藏域。
表单域流程处理完后,表单发出一个“
validate
”事件,这是一个执行跨表单验证(还不能公布其详情)的机会。
然后,表单确定是否存在任何验证错误。如果存在,表单提交失败并发出一个
"failure"
事件。如果没有验证错误,些时将
发出
一个
"success"
事件。
最后,表单发出一个 "submit"
事件(逻辑上,它不考虑成功与否)。
跟踪验证错误
一个与表单关联的就是验证跟踪器( ValidationTracker
),它跟踪着表单域对应的所有的用户输入与用户验证错误。跟踪器可以通过跟踪器参数提供给表单,不过很少用到。
表单( Form
)包括两个 isValid()
和 getHasErrors()
方法,用来查看表单验证跟踪器是否存在任何错误。
在我们的逻辑中,我们可以记录验证错误。表单( Form
)包括两个不同版本的 recordError()
方法,一个是指定一个表单域( Field
,一个被所有表单元素组件实现的接口),另外一个是全局验证错误( "global" errors
),与具体的表单域无关。
在请求间保存数据
因为其他的动作请求,表单提交的结果会向客户端发出一个重定向来重新呈现页面。验证跟踪器必须在请求间被持久化地( persistently
)保存下来,否则所有的验证信息会丢失(表单提供一个 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
表单重新呈现时被修饰以表现错误给用户看。
配置表单域及标注
页面模板包含少量的 Tapestry
标识( instrumentation
):
< 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
>
Tapestry
的 Form
组件负责创建 form
表单提交所需的 URL
(这个 Tapestry
的责任,不是你的)。
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
特定的值保证了模板的有效性。
错误及修饰Errors and Decorations
注意:这部分并没有更新到涉及客户端验证的介绍。
当你第一次激活 Login
页里,表单域及表单会被正常呈现等待输入:
注意这里的 Label
组件怎样显示表单域的文本名字。我们未曾做过任何显式地配置,组件的 id
( "User Name"
和 "Password"
)已经转换成 "User Name"
和 "Password"
,倒底发生了什么?。
如果你就这样提交了表单,表单域就会违反了 "required"
约束,页面将会重新显示以呈现给用户错误信息:
这里有两个微妙事情,首先, Tapestry
跟踪了所有表单域的所有错误信息, Errors
组件显示在表单的最顶端;其次,默认的验证修饰给标注( labels
)和表单域的 class
属性加上了 t-error CSS
样式。 Tapestry
提供了默认的 t-error CSS
样式使得它们变红。
接下来,我们将在 user name
栏上填值,但不给 password
栏提供足够的字符:
User name
栏正常,仅 password
栏有一个错误存在。
PasswordField
组件默认下通常显示一空值,另外我们会看到仅
password
显示在
…
如果你输入了足够的字符后提交,我们会看到
Login
页面是如何将逻辑错误加入到表单域的错误里:
这真是天衣无缝,基于应用的逻辑错误看起来感觉就像内置验证器的行为。