表格和验证

表格是大多数web应用程序从用户那里收集重要信息的传统方式。无论是搜索表单、登录屏幕还是多页注册向导,Tapestry都使用标准的HTML表单,默认情况下使用HTTPPOST操作。此外,支持使用基于AJAX的表单提交区域.

相关文章

Tapestry为创建和呈现表单、填充表单字段以及验证用户输入提供了支持。对于简单的情况,输入验证是声明性的,这意味着您只需告诉Tapestry要对给定字段应用哪些验证,它会在服务器上以及(可选)在客户机上进行处理。此外,您可以在页面或组件类中提供事件处理程序方法来处理更复杂的验证场景。

最后,Tapestry不仅可以方便地向用户显示错误消息,还可以在验证失败时自动突出显示表单字段。

目录

表单组件

Tapestry的表单支持的核心是表格组件。Form组件包围(环绕)所有其他组件现场组件例如文本字段,文本域,复选框等。

表单事件

Form组件发出许多组件事件。您需要为其中一些提供事件处理程序方法。

渲染时,Form组件会发出两个事件:首先是“prepareForRender”,然后是“preare”。这些允许表单的容器设置将在表单中引用的任何字段或属性。例如,这是创建要呈现的临时实体对象或从要编辑的数据库加载实体的好地方。

当用户在客户端提交表单时,服务器上会发生一系列步骤。

首先,表单发出“prepareForSubmit”事件,然后是“prepare”事件。这些允许容器确保对象已设置好并准备好接收来自表单提交的信息。

接下来,表单中的所有字段都是激活要从传入的请求中提取值,请验证它们并(如果有效)存储更改。

字段完成处理后,表单会发出一个“validate”事件。这是您执行任何无法以声明方式描述的跨表单验证的机会。

接下来,表单将确定是否存在任何验证错误。如果有,则提交被视为失败,并发出“失败”事件。如果没有验证错误,则会发出“成功”事件。

最后,表单发出一个“提交”事件,用于不关心成功或失败的逻辑。

表单事件(按顺序)

阶段

排放时(和典型使用)

方法名称@OnEvent常量

准备渲染

伦德尔

呈现表单之前(例如,从要编辑的数据库加载实体)

onPrepareForRender()事件常量。准备_发送

准备

伦德尔

呈现表单之前,但之后准备渲染

onPrepare()事件常量。准备

准备提交

提交

处理提交的表单之前

提交准备()事件常量。准备_提交

准备

提交

处理提交的表单之前,但之后准备提交

onPrepare()事件常量。准备

验证

提交

根据提交的值填充字段并验证后(例如,执行跨字段验证)

onValidate(启用验证)事件常量。验证

验证表单

提交

等同于验证(已弃用–请勿使用)

onValidateForm(验证表单)

失败

提交

发生一个或多个验证错误后

on失败()事件常量。故障

成功

提交

验证完成后没有任何错误(例如,保存对数据库的更改)

onSuccess()事件常量。成功

提交

提交

所有验证(成功或失败)完成后

onSubmit()事件常量。提交
取消提交每当提交链接提交组件包含mode=“取消”mode=“无条件”已单击on取消()事件常量。取消

请注意,“prepare”事件在表单呈现和表单提交期间发出。

处理事件

主要条款:组件事件

您可以通过在页面或组件类中提供方法来处理事件,方法如下事件发件人组件()命名约定或使用OnEvent注释。例如:

使用命名约定的事件处理程序
无效onValidateFromPassword(){…}

或使用@OnEvent的等效方法:

使用@OnEvent注释的事件处理程序
@OnEvent(值=EventConstants.VALIDATE,组件=“密码”)无效验证密码(){…}

跟踪验证错误

与表单关联的是ValidationTracker(验证跟踪程序)跟踪表单中每个字段的所有用户输入和验证错误。跟踪器可以通过表单的跟踪器参数提供给表单,但这很少是必要的。

表格包括方法有效()获取有误信息(),用于查看表单的验证跟踪器是否包含任何错误。

在你自己的逻辑中,记录你自己的错误是可能的。表单包括方法的两个不同版本记录错误(),其中一个指定了字段(由所有表单元素组件实现的接口),其中一个用于“全局”错误,与任何特定字段无关。如果错误只涉及单个字段,则应使用第一个版本,以便突出显示该字段。

在请求之间存储数据

与其他操作请求一样,表单提交的结果(使用区域)是向客户端发送重定向,这将导致第二个请求(重新发送页面)。ValidationTracker必须是坚持(通常在HttpSession中)跨越这两个请求,以防止验证信息丢失。幸运的是,Form组件提供的默认ValidationTracker是持久的,所以您通常不必担心它。

然而,出于同样的原因,组件更新的各个字段也应该跨请求持久化,这就是您通常使用@Persist注释。

例如,收集用户名和密码的Login页面类可能如下所示:

Login.java示例
包com.example.newapp.pages;导入com.example.newapp.services。UserAuthenticator;导入org.apache.tapestry5.annotations.*;导入org.apache.tapestry5.corelib.components。形式;导入org.apache.tapestry5.corelib.components。密码字段;导入org.apache.tapestry5.ioc.annotations。注入;公共类登录{@坚持@财产私有字符串用户名;@财产private字符串密码;@注入私有UserAuthenticator验证器;@注入组件(“密码”)private PasswordField密码字段;@组件私有表单登录表单;/***进行跨字段验证*/从登录表单()验证时无效{if(!authenticator.isValid(用户名,密码)){//记录错误,从而防止Tapestry发出“成功”事件loginForm.recordError(passwordField,“用户名或密码无效。”);}}/***验证通过,因此我们将转到“PostLogin”页面*/对象onSuccess(){return PostLogin.class;}}

因为表单提交实际上请求:提交本身(导致重定向响应),然后是对页面的第二个请求(导致页面的重新呈现),需要使用@persist注释在两个请求之间持久化userName字段。这对于密码字段也是必需的,除了密码字段组件从不呈现值。

为了避免数据丢失,其值存储在HttpSession中的字段(如上面的userName)必须是可序列化的,特别是如果您希望能够集群应用程序或跨服务器重启保留会话。

如果之前没有验证错误,表单只会发出“成功”事件。这意味着没有必要编写if(form.getHasErrors())return;作为方法的第一行。

最后,请注意业务逻辑如何适应验证。UserAuthenticator服务负责确保用户名和(明文)密码有效。当它返回false时,我们要求Form组件记录错误。我们提供PasswordField实例作为第一个参数;这样可以确保在重新呈现表单时修饰密码字段及其标签,以向用户显示错误。

配置字段和标签

下面的Login页面模板包含最少的Tapestry工具,并引用了一些引导数据库CSS类(默认情况下,Bootstrap会自动集成到每个页面中,从Tapestry 5.4开始)。

Login.tml示例
<html t:type=“layout”title=“newapp com.example”xmlns:t=“http://tapestry.apache.org/schema/tapestry_5_4.xsd"><div class=“row”><div class=“span4 offset3”><t:form t:id=“loginForm”><h2>请登录</h2><t:textfield t:id=“userName”t:mixins=“formgroup”/><t:passwordfieldt:id=“password”value=“password”t:mixins=“formgroup”/><t:submit class=“btn-btn-large btn-primary”value=“登录”/></t:form></div></div></html>

对页面进行渲染可以获得令人满意的第一步:

Tapestry Form组件负责为表单提交创建必要的URL(这是Tapestry's的责任,而不是您的责任)。

对于TextField,我们提供了一个组件id userName。我们可以指定价值参数,但默认情况下,如果存在这样的属性,则根据容器的属性(Login页面)匹配TextField的id。 

根据经验,您应该始终为字段指定一个特定的id(该id将用于生成名称身份证件渲染标记的属性)。允许省略value参数有助于避免模板变得过于混乱。

FormGroup mixin用一些额外的标记装饰字段,包括一个<label>元素;这利用了更多的Bootstrap。

呈现的userName组件
<div class=“form-group”>用户名<input id=“userName”class=“form-control”name=“userName“type=”text“></div>

表单验证

上面的示例是一个非常基本的表单,允许字段为空。然而,只要稍加努力,我们就可以添加客户端验证,以防止用户提交任何字段为空的表单。

Tapestry中的验证涉及关联一个或多个验证器使用表单元素组件,如TextField或PasswordField。这是使用验证参数:

<t:textfield t:id=“userName”validate=“required”t:mixins=“formgroup”/><t:passwordfield t:id=“password”value=“passward”validate=“required”t:mixins=“formgroup”/>

可用验证程序

Tapestry提供了以下内置的验证器:

验证器

约束类型

说明

例子

电子邮件

确保给定的输入看起来像有效的电子邮件地址

<t:textfield value=“userEmail”validate=“email”/>

最大值

长的

强制使用最大整数值

<t:textfield value=“age”validate=“max=120,min=0”/>

最大长度

整数

确保字符串值具有最大长度

<t:textfield value=“zip”validate=“maxlength=7”/>

最小值

长的

强制最小整数值

<t:textfield value=“age”validate=“max=120,min=0”/>

最小长度

整数

确保字符串值具有最小长度

<t:textfield value=“somefield”validate=“minlength=1”/>

没有人

不执行任何操作(用于覆盖@Validate注释)

<t:textfield value=“somefield”validate=“none”/>

正则表达式

图案

确保字符串值符合给定的模式

<t:textfield value=“letterfield”validate=“regexp=^[A-Za-z]+$“/>

必修的

确保字符串值不为null,也不为空字符串

<t:textfield value=“name”validate=“required”/>

已检查(自5.4.5起)布尔值确保布尔值为true(选中复选框)<t:复选框value=“value”validate=“checked”/>
未选中(自5.4.5起)布尔值确保布尔值为false(复选框未选中)<t:复选框value=“value”validate=“unchecked”/>

使用@Validate集中验证

@验证注释可以代替TextField、PasswordField和TextArea等组件的验证参数。当模板文件中未绑定validate参数时,组件将检查@validate注释并使用其值作为验证定义。

注释可以放在getter或setter方法上,也可以放在字段本身上。

让我们更新登录页面的两个字段:

@坚持@财产@验证(“必需”)私有字符串用户名;@财产@验证(“必需”)私有字符串密码;

现在,我们将重建应用程序,刷新浏览器,然后按enter键:

表单已更新到位,以显示错误。在为每个字段提供一些值之前,您将无法提交表单。

HTML5客户端验证

tapestry.enable-html5-支持 配置符号设置为true(它是默认情况下),Tapestry的内置验证器将自动为Tapestry's表单组件的呈现HTML启用HTML5特定的“类型”和验证属性,触发大多数现代浏览器内置的HTML5客户端验证行为。例如,如果您使用“电子邮件”和“必需”验证器,如下所示:

<t:textfield validate=“电子邮件,必填”…/>

则输出HTML如下所示:

需要<input type=“email”…>

这会导致现代浏览器在输入看起来不像电子邮件地址的文本时,或在字段留空时,都会显示验证错误消息。

执行浏览器的内置验证之前Tapestry自己的客户端验证。这样,较旧的浏览器仍将按预期执行客户端验证。

包括以下行为:

  • 必修的验证器将“required”属性添加到呈现的HTML
  • 已检查验证器将“required”属性添加到呈现的HTML(自5.4.5起)

  • 正则表达式验证器将“pattern”属性添加到呈现的HTML
  • 电子邮件“验证器设置类型属性设置为呈现的HTML中的“电子邮件”
  • 最小值“验证器设置类型属性设置为“number”,并在呈现的HTML中添加“min”属性
  • 最大值“验证器设置类型属性设置为“number”,并在呈现的HTML中添加“max”属性
  • 当绑定到类型,TextField组件设置类型属性设置为呈现的HTML中的“数字”

服务器端验证

有些验证不能或不应该在客户端进行。我们如何知道密码是否正确?除了将所有用户和密码下载到客户端之外,我们还需要在服务器上进行验证。

事实上,所有客户端验证(通过validate参数或@validate注释)都会在服务器上再次执行。

也可以在那里执行额外的验证。

/***进行跨场验证*/从登录表单()验证时无效{if(!authenticator.isValid(用户名,密码)){//记录错误,从而防止Tapestry发出“成功”事件loginForm.recordError(passwordField,“用户名或密码无效。”);}}

这是loginForm组件中的验证事件处理程序。一旦所有组件都有机会从请求中读取值,进行自己的验证,并更新绑定到的属性,就会调用它。

在这种情况下,验证器用于确定用户名和密码是否有效。在实际应用程序中,这将是查询数据库或其他外部服务的地方。

如果组合无效,则密码字段标记为错误。该表单用于记录有关组件(passwordField)的错误,并显示错误消息。

在表格中输入任意两个值并提交将导致往返;表单将重新显示,以向用户显示错误:

请注意,光标直接放置在密码字段中。

在5.4之前的Tapestry版本中,带有验证错误的表单将导致对客户端的重定向响应;通常,临时服务器端数据(例如userName字段)会丢失。从5.4开始,提交带有验证错误的表单会导致在提交表单的同一请求中呈现新页面。

自定义验证消息

当违反约束时,每个验证器(例如“required”或“minlength”)都会使用默认消息(在客户端和服务器端);也就是说,当用户输入无效时。

可以通过向页面的消息目录(或包含组件的消息目录)。与任何本地化属性一样,这也可以进入应用程序的消息目录。

检查的第一个键是表单Id-字段Id-验证器名称-消息。

  • formId:Form组件的本地组件id
  • fieldId:字段的本地组件id(TextField等)
  • validatorName:验证器的名称,即“required”或“minlength”

如果该密钥没有消息,则进行第二次检查字段Id-验证器名称-消息。 如果如果与消息不匹配,则使用内置的默认验证消息。

例如,如果表单ID是“loginForm”,字段ID是“userName”,验证器是“required”,Tapestry将首先在消息目录中查找“loginForm-userName-required-message”键,然后查找“userName-required-message“键。

消息目录中的验证消息可能包含打印样式格式字符串(例如%s)以指示将在何处插入验证参数的值。例如,如果模板中的validate参数为minLength=3,验证消息为“用户名必须至少为%s个字符”,则相应的错误消息为“用户名必须至少包含5个字符”。

自定义BeanEditForm的验证消息

这个BeanEditForm(BeanEdit表单)组件还支持验证消息定制。信息搜索类似;这个表单ID是BeanEditForm组件(而不是它包含的Form组件)的组件id。这个字段Id是属性名称。

在消息目录中配置验证程序约束

可以从验证参数(或@Validator注释)中省略验证约束,在这种情况下,它应该存储在消息目录中。

当验证约束难以进入内联时,这很有用,例如用于regexp验证器的正则表达式。

此处的键与自定义验证消息类似:表单Id-字段Id-验证器名称或者只是字段Id-验证器名称.

例如,您的模板可能具有以下内容:

<t:textfield t:id=“ssn”validate=“必需,regexp”/>

您的邮件目录可以包含:

ssn-regexp=\d{3}-\d日{2}-\d{4}ssn-regexp-message=社会保险号码的格式为12-34-5678。

此技术也适用于BeanEditForm;与验证消息一样,formId是BeanEditForm组件的id,fieldId是正在编辑的属性的名称。

验证宏

5.2中增加

验证器列表可以组合到验证宏。此机制便于确保应用程序中的验证规则一致。要创建验证宏,只需在模块类(通常为AppModule.java)中为ValidatorMacro服务添加一个新条目,如下所示。第一个参数是宏的名称,第二个参数是以逗号分隔的验证器列表:

AppModule.java(部分)
@贡献(ValidatorMacro.class)公共静态void组合PasswordValidators(MappedConfiguration<String,String>configuration){configuration.add(“passwordValidator”,“必需,minlength=5,maxlength=15”);}

然后,您可以在组件模板和类中使用此新宏:

<input t:type=“textField”t:id=“password”t:验证=“passwortValidator”/>
@验证(“密码”)private字符串密码;

用事件覆盖转换器

TextField、PasswordField和TextArea组件都有一个translate参数字段转换器对象,用于将服务器端的值转换为客户端的字符串。

在大多数情况下,translate参数没有显式设置;Tapestry根据字段编辑的属性类型派生适当的值。

在某些情况下,您可能希望覆盖转换器。这可以通过组件上触发的两个事件“toclient”和“parseclient”来完成。

“toclient”事件被传递给当前对象值并返回一个字符串,这将是字段的默认值。如果没有事件处理程序,或者事件处理程序返回null,则使用默认的Translator将服务器端值转换为字符串。

例如,您可能希望数量字段最初显示为空白而不是零:

<t:textfield t:id=“quantity”size=“10”/>. . .私有int数量;ToClientFromQuantity()上的字符串{如果(数量==0),则返回“”;返回null;}

到目前为止,这是很好的,但是如果字段是可选的,并且用户提交了表单,您将得到一个验证错误,因为空字符串作为整数是无效的。

这就是“parseclient”事件出现的地方:

ParseClientFromQuantity上的对象(字符串输入){if(“”.equals(input))返回0;返回null;}

事件处理程序方法优先于转换器。这里它检查空字符串(注意输入可能为null!)并将其计算为零。

同样,返回null可以让普通的转换器完成其工作。

事件处理程序还可能引发验证异常以指示无法解析的值。

现在,如果您想执行自己的自定义验证,该怎么办?这是另一个事件:“验证”:

void onValidateFromCount(整数值)引发ValidationException{if(value.equals(13))抛出新的ValidationException(“13是一个不吉利的数字。”);}

此事件被触发之后普通验证器。它通过了已解析值(不是来自客户端的字符串,而是来自转换器或“parseclient”事件处理程序的对象值)。

该方法可能不会返回值,但可能会抛出ValidationException以指示值有问题。

注意:这些活动仅在服务器端这意味着,在某些情况下,即使输入值在服务器端有效,在客户端也会被拒绝。您可能需要禁用客户端验证才能使用此功能。