用户输入的数据有时候并不满足应用程序的规定,在 Flex 中验证器用来保证 UI 中的数据满足某种规则。比如,你可以用一个数字验证器来保证输入的只能是在某个范围内的数字。在典型的客户端 - 服务器环境中,数据的验证发生在服务器端。Flex 验证器的好处是你可以在客户端就进行一些基本的数据验证,而不用来回的往服务器端发送不合理的数据。
Flex 提供了一些基本的验证器供用户使用。你可以扩展它们来实现更复杂的验证,甚至重新创建自定义规则的验证器。
验证器需要与目标源关联,而目标源包含一个需要验证的属性。目标源可以是一个 UI 组件,也可以是一个数据模型。当验证器与一个目标源的属性关联之后,每次该属性值的改变都会触发验证器的验证。验证结果通过 valid和 invalid事件派发。valid事件表明验证通过,invalid事件表明验证失败。
虽然 Flex 验证器可以自动地工作,但它没有明确的可绑定的属性来表示验证结果,而仅仅是派发验证结果事件。另外,每个验证器独立的工作,如果有很多个验证器,怎么来统一管理,Flex 也没有相应的类提供支持。所以本文提出构建一个自动验证框架来统一管理多个验证器,在实践中证明是很有效的数据验证工具。
在 web 开发中,表格非常的普遍。在一个表格中存在着多个输入框,需要验证用户输入。全部验证通过才能进行下一个步骤。用 Flex 来写一个表格非常的简单,只需要调用现成的 Form组件。但是要同时对所有的输入框进行验证,则需要大量的逻辑代码。下面我们来模拟一个简单的添加数据库连接表格,以此说明构建一个验证器框架的重要性。
图 1. 添加数据库连接表格
此表格包含 4 个输入框,2 个按钮。全部输入合法之后,按钮才能被激活。它的 MXML 代码如下。
清单 1. 表格代码
<?xml version=”1.0” encoding=”utf-8”?> <mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” xmlns:validator=”com.ibm.rdf.validator.*”> <mx:Panel> <mx:Form> <mx:FormHeading label=”Add Connection”/> <mx:FormItem label=”Connection Name”> <mx:TextInput id=”connName”/> </mx:FormItem> <mx:FormItem label=”Database Name”> <mx:TextInput id=”dbName”/> </mx:FormItem> <mx:FormItem label=”Host Name”> <mx:TextInput id=”4ostname”/> </mx:FormItem> <mx:FormItem label=”Port Number”> <mx:TextInput id=”port”/> </mx:FormItem> </mx:Form> <mx:ControlBar horizontalAlign=”right”> <mx:Button id=”okBtn” label=”OK”/> <mx:Button id=”cancelBtn” label=”Cancel”/> </mx:ControlBar> </mx:Panel> </mx:Application> |
-
没有验证框架代码示例
-
为每个输入框关联一个验证器,并用一个布尔变量来保存它的验证结果。
清单 2. 添加验证器代码Private var connNameValid:Boolean = false; private var dbNameValid:Boolean = false; private var hostValid:Boolean = false; private var portValid:Boolean = false; <mx:StringValidator source=”{connName}” property=”text”/> <mx:StringValidator source=”{dbName}” property=”text”/> <validator:RDFHostValidator source=”{4ostname}” property=”text”/> <mx:NumberValidator source=”{port}” property=”text” allowNegative=”false” minValue=”1” maxValue=”65535” domain=”int”/> -
监听每个验证器的
valid和invalid事件,根据验证结果改变其对应的布尔变量的值,并根据所有布尔变量的值来设置按钮状态。
清单 3. 逻辑代码<mx:StringValidator id=”connNameValidator” source=”{connName}” property=”text” valid=”setValid(event)” invalid=”setValid(event)”/> <mx:StringValidator id=”dbNameValidator” source=”{dbName}” property=”text” valid=”setValid(event)” invalid=”setValid(event)”/> <validator:RDFHostValidator id=”hostValidator” source=”{4ostname}” property=”text” valid=”setValid(event)” invalid=”setValid(event)”/> <mx:NumberValidator id=”portValidator” source=”{port}” property=”text” allowNegative=”false” minValue=”1” maxValue=”65535” domain=”int” valid=”setValid(event)” invalid=”setValid(event)”/> private function enableButtons():void { if (connNameValid && dbNameValid && hostValid && portValid) { okBtn.enabled = true;} else { okBtn.enabled = false; } } private function setValid(event:ValidationResultEvent):void { if (event.currentTarget == connNameValidator) { connNameValid = changeValid(event); } else if (event.currentTarget == dbNameValidator) { dbNameValid = changeValid(event); } else if (event.currentTarget == portValidator) { portValid = changeValid(event); } else { hostValid = changeValid(event); } enableButtons(); } private function changeValid(event:ValidationResultEvent):Boolean { if (event.type == ValidationResultEvent.VALID) return true; else return false; } -
页面创建完成时设置按钮的初始状态。
清单 4. 页面创建代码<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” xmlns:validator=”com.ibm.rdf.validator.*” creationComplete=”enableButtons()”>
-
为每个输入框关联一个验证器,并用一个布尔变量来保存它的验证结果。
- 使用验证框架代码示例
只需将所有验证器置放于验证框架内,再用数据绑定把按钮的 enabled 属性与验证器的验证结果绑定起来,按钮的状态就会根据验证结果自动改变。
清单 5. 使用验证框架代码
<validator:RDFValidators id=”validators”>
<mx:StringValidator source=”{connName}” property=”text”/>
<mx:StringValidator source=”{dbName}” property=”text”/>
<validator:RDFHostValidator source=”{4ostname}” property=”text”/>
<mx:NumberValidator source=”{port}” property=”text”
allowNegative=”false” minValue=”1” maxValue=”65535” domain=”int”/>
</validator:RDFValidators>
<mx:Button label=”OK” enabled=”{validators.valid}”/>
<mx:Button label=”Cancel”/>
|
从这个简单的示例可以看出验证器框架减少了大量的逻辑代码,从而大大提高了编程效率。特别是多个验证器同时工作的情况下非常实用。
本章中会介绍构建自动框架的具体实现。
- 自动验证框架最重要的接口就是 valid 属性。它是一个可绑定的布尔值,会随着其包含的所有验证器的验证结果自动变化。注意,它是一个只读属性。因为它的值只会忠实于所包含的验证器,所以不能被人为的更改。
在 Flex 中,只有可读写的属性才能是可绑定的,如何使只读属性可绑定将在下面的设计核心一节中介绍。
- 一个名为 validatorArray 的数组来放置验证器。
- 适时的事件派发。除了转发每个验证器的 valid 和 invalid 事件之外,它还在自己的 valid 值改变的时候派发 validChanged 事件。用户捕获这个事件之后就可以调用相应的逻辑代码。
继承 EventDispatcher 实现 IMXMLObject 接口
验证框架类没有继承 Flex 的 Validator 类,因为它本身并不是一个验证器,而是管理验证器的框架。它直接继承 EventDispatcher 以实现派发事件的功能。在 Flex 中,所有不包含 UI 的组件都必须继承 IMXMLObject,才可以在 MXML 中使用。所以它实现了 IMXMLObject 接口,该接口只有一个 initialized()方法。
清单 6. 实现 IMXMLObject 接口代码
/**
* @private
* 实现 IMXMLObject
*/
public function initialized(document:Object, id:String):void
{
this.id = id;
this.document = document;
}
|
Flex 验证器本身是采用观察者模式,当它关联的目标源的输入值改变时,触发验证,它派发验证结果事件。验证框架也沿袭观察者模式,对其管理的验证器数组中的每个验证器进行监听。根据本次接收到的事件和保存的其它验证结果事件,计算当前综合的验证结果,派发 valid 和 invalid 事件。如果验证结果与上次结果不同,还派发 validChanged 事件。
图 2. 验证框架构造示意图
- 强制验证的验证器数目 requiredNum
验证器的 required 属性决定此验证器是否强制验证。如果 required 值为真,那么此验证器关联的源目标值不能为空,否则验证失败。反之,源目标值为空的时候也能通过验证。requiredNum 用于保存 required 值为真的验证器的数目。既然验证框架是被动的观察者,它必须知道某些被观察者没有派发任何事件的情况下,表示验证是通过的。因此,验证框架也必须计算 required 为真的验证器的数量,当收到的事件数目小于它时,验证失败;反之,检查是否每个 required 值为真的验证器的结果。
- 验证器数组 validatorArray
用 setter/getter存储元访问可读写的 validatorArray属性。这样,在 setter中就可以方便的加入逻辑代码。在这里我们需要对数组中的每个验证器添加 valid和 invalid事件监听器。同时也需要计算 requiredNum的值以便后续使用。
用标签 [DefaultProperty('validatorArray')],把验证器数组设为验证器框架类的默认值。这样用户就可以直接在 MXML 里直接把验证器放置在框架类的标签中,不需要另写 < validator:validatorArray ></ validator: validatorArray >标签来放验证器。
清单 7. 写 validatorArray 代码
public function set validatorArray(validators:Array):void
{
this._validatorArray = validators;
var i:int = 0;
requiredNum = 0;
for each (var v:EventDispatcher in validators)
{
// 计算 requiredNum 值
……
v.addEventListener(ValidationResultEvent.INVALID, resultHandler);
v.addEventListener(ValidationResultEvent.VALID, resultHandler);
}
if (requiredNum == 0)
{
_required = false;
_valid = true;
}
}
|
-
验证结果数组
results
每次监听到 valid和 invalid事件就把当次的验证结果放入验证结果数组中。验证结果数组提供了计算验证框架综合结果的依据。
- 计算已经验证过的验证器数目
requiredValidatedNum保存收到的 required值为真的验证器数目。如果已经验证过的验证器数目 requiredValidatedNum小于强制验证的验证器数目 requiredNum,那么验证结果肯定是失败的。当 requiredValidatedNum等于 requiredNum时,我们需要浏览验证结果数组中的每个结果来计算验证框架的结果。
-
验证结果
ValidationResultEvent提供的信息
Flex 验证结果 ValidationResultEvent事件的 currentTarget属性包含目标实例,即是哪个源目标触发了验证事件。我们通过比较监听到的事件和验证结果数组 results中保存的事件的触发源,就可以知道当前事件是新事件还是旧事件值的改变。如果是新事件,我们需要把它保存在验证结果数组中;如果是已经保存过的事件,我们需要更新保存的值。
验证结果 ValidationResultEvent事件的 type属性指出该次验证是成功(valid)还是失败(invalid)。我们每次监听到事件,都根据所有保存事件的验证值重新计算验证框架的验证结果。
清单 8. 利用验证结果事件代码
if (results == null || results.length != validatorArray.length)
{
var alreadyIn:Boolean = false;
for (var i:int = 0; i < results.length; i++)
{
// 比较发出事件的目标源
if (results[i].currentTarget == event.currentTarget)
{
results[i] = event;
alreadyIn = true;
}
}
if (!alreadyIn)
{
results.push(event);
}
}
|
-
实现
valid属性可绑定
属性可绑定表示该属性可以作为数据绑定的源。数据绑定就是将数据从源位置拷贝到目标位置,从而实现目标位置的数据保持与源位置的数据一致。bindable对于 valid属性非常重要。它必须是 bindable才能绑定到 UI 组件上,让用户在任何时候看到的都是最新的值。
valid属性只有 getter,是只读属性。一般来说,属性的 getter绑定到 setter上可以实现可绑定(bindable)。在没有 setter的情况下,我们把 valid属性绑定到 validChanged事件。每当验证框架的 valid值改变时都会触发 validChanged 事件。也即是每当写 valid值时,都会有 validChanged事件派发出来。所以我们把 valid属性绑定到 validChanged事件上就可以实现 valid 属性的 bindable了,可以用标签来实现 [Bindable("validChanged")]。
清单 9. 实现可绑定的 valid 属性代码
[Bindable("validChanged")]
/**
* 验证框架的验证结果
*/
public function get valid():Boolean
{
return this._valid;
}
|
-
计算
valid值
验证框架的价值就在于它把其包含的验证器进行统一的管理,提供一个整体的结果,而这个结果用 valid值来表示。每次监听到验证器的验证事件时,都需要重新计算 valid值,其步骤如下:
-
根据当前收到的验证结果与
results中的所有结果进行比较,如果是新结果就直接放进results数组,如果是新数据就更新它。 -
计算已经验证过的验证器数目
requiredValidatedNum和强制验证的验证器数目requiredNum。 -
如果
requiredValidatedNum小于requiredNum,说明还有不允许为空的数据源没有数据,验证结果是失败的。 -
如果
requiredValidatedNum等于requiredNum,遍历results数组并把当前收到的验证结果更新到results数组中,对所有验证结果进行逻辑运算计算出总体结果。 -
如果刚计算出的总体结果不同于当前
valid值,更新valid值,并派发validChanged事件。 -
如果 valid值为真,派发valid事件。 -
如果
valid值为假,派发invalid事件。
清单 10. 计算 valid 值代码
protected function resultHandler (event:ValidationResultEvent):void
{
// 还没有验证过验证框架内所有的验证器,需要对验证结果进行整理
if (results == null || results.length != validatorArray.length)
{
var alreadyIn:Boolean = false;
var r:ValidationResultEvent;
for (var i:int = 0; i < results.length; i++)
{
// 派发事件的目标源
if (results[i].currentTarget == event.currentTarget)
{
results[i] = event;
alreadyIn = true;
}
}
if (!alreadyIn)
{
results.push(event);
}
}
// 计算 requiredValidatedNum 值和 requiredNum 值
……
// 强制验证的验证器都验证过之后可以计算总体结果
if (requiredValidatedNum == requiredNum)
{
var wholeValid:Boolean = true;
for (var j:int = 0; j < results.length; j++)
{
……
var isValid:Boolean = results[j].type == ValidationResultEvent.VALID;
wholeValid = wholeValid && isValid;
}
if (valid != wholeValid)
{
this._valid = wholeValid;
dispatchEvent(new Event(“validChanged”));
}
if (valid)
{
dispatchEvent(new ValidationResultEvent(“valid”));
}
if (!valid)
{
dispatchEvent(new ValidationResultEvent(“invalid”));
}
}
}
|
前章中阐述了自动验证框架的基本设计,为了适应更复杂的用户需求,我们可以在此基础上使它更加完善。
一般来说,我们需要验证框架包括的所有验证器都验证通过时,总体 valid结果才为真。但在某些情况下,只要某一个验证器通过验证,就可以认为整个验证通过了。从逻辑上看,一般我们考虑的是与(AND)运算,但或(OR)运算也有其实用价值。
为了支持用户选用与或运算,我们需要提供一个变量 logic来保存逻辑运算的方式。另外还提供两个静态常量 AND和 OR来表示与或运算。logic缺省值为 AND
前章阐述的实现对于与或运算都适用。只是在计算 valid值时不需要第 2,3 步,第 4 步选用不同的逻辑运算方式。除此之外,注意到 Flex 的验证器会自动对验证失败的目标源组件添加 errorString。导致目标源组件呈现红色的验证失败提示框。所以我们需要额外地清除这些 errorString,以免误导用户。
清单11. 清除errorString代码
protected function cleanErrorMessages():void
{
for each (var rr:ValidationResultEvent in results)
{
if (rr.type == ValidationResultEvent.INVALID)
{
if (rr.currentTarget is Validator)
{
//将rr的results属性置空
//清空目标源的控件的errorString属性
}
else if (rr.currentTarget is RDFValidators)
{
(rr.currentTarget as RDFValidators).cleanErrorMessages();
}
}
}
}
|
图 3. 出现红色的验证失败提示框的表格
前章我们把验证框架设计成可以管理验证器的容器,我们通过监听验证器的 valid和 invalid事件来触发容器的管理。从广义上来说,只要能够派发 valid和 invalid事件的组件都可以被管理。那么我们的验证框架也可以被管理,这样我们就能实现嵌套框架,组合出更多的逻辑运算。
在计算验证框架的 valid值时,我们用到了每个验证器的 required属性。required属性表明该验证器的目标源数据是否可以为空,它对于验证框架来说也是有意义的。所以我们可以赋予验证框架一个只读的 required属性,它根据自身管理的验证器来决定自己的 required属性值。只要有一个验证器是强制验证的,管理它的验证框架 required值就是真。否则相反。
图 4. 嵌套框架示意图
下面通过一个实例来展示:
清单 12. 嵌套框架的示例代码
<mx:Panel>
<mx:Form>
<mx:FormHeading label="Input your information"/>
<mx:Label text="Input your either your name or your ID"/>
<mx:HBox>
<mx:FormItem label="Name">
<mx:TextInput id="myname"/>
</mx:FormItem>
<mx:FormItem label="ID">
<mx:TextInput id="myid"/>
</mx:FormItem>
</mx:HBox>
<mx:FormItem label="Email" required="true">
<mx:TextInput id="myemail"/>
</mx:FormItem>
</mx:Form>
<mx:ControlBar horizontalAlign="right">
<mx:Button label="OK" enabled="{validators2.valid}"/>
<mx:Button label="Cancel"/>
</mx:ControlBar>
</mx:Panel>
<validator:RDFValidators id="validators2">
<validator:RDFValidators logic="or">
<mx:StringValidator id="nameValidator" source="{myname}"
property="text" triggerEvent="change"/>
<mx:StringValidator id="idValidator" source="{myid}"
property="text" triggerEvent="change"/>
</validator:RDFValidators>
<mx:EmailValidator id="emailValidator" source="{myemail}"
property="text" triggerEvent="change"/>
</validator:RDFValidators>
|
图 5. 示例代码显示界面
本文阐述了构建 Flex 自动验证框架以实现对多个验证器的统一管理 , 并通过示例演示了自动验证框架在代码中的使用。希望通过自动验证框架的使用,可以帮助您更高效地开发 Flex 应用。
学习
-
参考 Adobe Flex的官方开发文档。
-
参考 Adobe Flex 开发官网。
-
参考 Flex Example网站相关资料。
-
访问 developerWorks Web Development专区,学习更多 WEB 开发技术。
-
developerWorks Web development
专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
-
developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
-
developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。
- 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。
讨论
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
