Tapestry的数据校验功能
数据校验是Web应用的一个很重要的功能。
tapestry支持配置式的数据校验,通过使用Delegate、FieldLabel和TextField对数据的显示进行修饰。在page文件中对各输入数据的校验规则进行配置。
可以使用系统提供的Validator。出错信息也提供了多种语言的版本,很方便。
但是问题是:如果表单有多个Submit按钮,不同的按钮需要的校验规则不同,此时该如何做?
众多的web开源框架都存在这样的问题,包括Struts、spring等。
我觉得表单的校验规则应当基于每个提交动作进行配置,而不是基于表单的所有提交动作。
这个现象普遍存在我觉得很奇怪,难道老外做的页面都只有一个提交按钮?
Matt Raible在使用struts中也遇到这个问题,他的解决办法是用javascript给按钮增加onclick代码。这种做法不是太好。
一个想法是取消在page文件中对数据校验的设置,改为在监听方法中调用系统提供的校验器进行校验。然后将错误信息记录于delegate。
如以下代码:
StringValidator stringValidator = new StringValidator();
stringValidator.setMinimumLength(6);
stringValidator.setRequired(true);
try {
stringValidator.toObject((IFormComponent) this
.getComponent("username"), getUsername());
} catch (ValidatorException ex) { delegate.record((IFormComponent)this.getComponent("username"),
ex.getLocalizedMessage());
}
但这么作丧失了配置的便利性。
现在正深入研究如何在tapestry更好地解决这个问题......
目前我的做法如下:
1,page中删除对各个输入域的校验配置,配置form的focus为true以便校验失败时自动聚焦到出错的输入域,注意页面必须使用Body组件;
<component id="loginForm" type="Form">
<binding name="delegate" value="bean:delegate"/>
<binding name="focus" value="true"/>
</component>
2,在各个监听方法中对需要校验的输入域调用系统的validator进行校验;
public static boolean required(ValidationDelegate delegate,
IFormComponent component, String value) {
boolean success = true;
StringValidator validator = new StringValidator();
validator.setRequired(true);
try {
validator.toObject(component, value);
} catch (ValidatorException ex) {
success = false;
delegate.record(component, ex.getLocalizedMessage());
delegate.registerForFocus(component, 0);
}
return success;
}
以上是对“必须”校验的简单封装,其中加粗一行是为了form聚焦用。
其它校验都可以分别进行类似的封装。
监听方法中可以这么调用各校验方法后,然后检查delegate是否有校验错误而进行不同的处理。通常的处理是如果有错误,则返回当前页面,否则进行相应的业务逻辑。
3,校验错误信息的显示
提供了一个ShowError组件:
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
if (cycle.isRewinding()) {
return;
}
IValidationDelegate delegate = getDelegate();
if (delegate == null || !delegate.getHasErrors()) {
return;
} else {
List fieldTrackings = delegate.getFieldTracking();
for (int i = 0; fieldTrackings != null && i < fieldTrackings.size(); i++) {
IFieldTracking fieldTracking = (IFieldTracking) fieldTrackings
.get(i);
if (fieldTracking.isInError()) {
writer.begin("label");
writer.attribute("for", fieldTracking.getFieldName());
writer.begin("span");
writer
.attribute("style",
"font-weight: normal; text-align: left; color: rgb(255, 0, 0); cursor: pointer;");
writer.print(fieldTracking.getErrorRenderer().toString());
writer.end();
writer.end();
writer.printRaw("<br>");
}
}
return;
}
}
实现的功能就是鼠标移动到每条错误信息上显示手形,点击鼠标自动聚焦到相关输入域上。
4,校验失败的输入域的样式修改
参考betterpetshop
public void writeAttributes(IMarkupWriter writer, IRequestCycle cycle,
IFormComponent component, IValidator validator) {
if (isInError(component)) {
writer.attribute("style",
"color: White; background-color: #EC6233;");
}
}
public void writeSuffix(IMarkupWriter writer, IRequestCycle cycle,
IFormComponent component, IValidator validator) {
if (isInError(component)) {
writer.print(" ");
writer.beginEmpty("img");
writer.attribute("src", "./imgs/bzzt_wrong.gif");
writer.attribute("height", 16);
writer.attribute("width", 16);
}
}
public void writeLabelPrefix(IFormComponent component,
IMarkupWriter writer, IRequestCycle cycle) {
if (isInError(component)) {
writer.begin("span");
writer.attribute("style", "color: #EC6233;");
}
}
public void writeLabelSuffix(IFormComponent component,
IMarkupWriter writer, IRequestCycle cycle) {
if (isInError(component))
writer.end();
}
这么一来,关于校验处理和错误信息显示基本上搞定了。