Flash 使用Flex 3处理客户端和服务器验证?
我是刚毕业的,所以请友善一点。我正在验证用户可以编辑的FlexDataGrid单元格中的输入。DataGrid中的行由一个Flash 使用Flex 3处理客户端和服务器验证?,flash,apache-flex,actionscript-3,design-patterns,flex3,Flash,Apache Flex,Actionscript 3,Design Patterns,Flex3,我是刚毕业的,所以请友善一点。我正在验证用户可以编辑的FlexDataGrid单元格中的输入。DataGrid中的行由一个mx.collections.ArrayCollection支持,它包括我编写的[Bindable]模型。我想根据验证,当且仅当通过时,我想验证服务器上的输入。如果客户端验证失败,我想显示正常的验证错误(见下图)。如果服务器端验证失败,我希望使用相同种类的UI组件通知用户。解决方案不应包括任何外部框架(或) 我的DataGrid实现是: <mx:DataGrid id
mx.collections.ArrayCollection
支持,它包括我编写的[Bindable]模型。我想根据验证,当且仅当通过时,我想验证服务器上的输入。如果客户端验证失败,我想显示正常的验证错误(见下图)。如果服务器端验证失败,我希望使用相同种类的UI组件通知用户。解决方案不应包括任何外部框架(或)
我的DataGrid实现是:
<mx:DataGrid id="myPageGrid" dataProvider="{myModelList}" editable="true"
itemEditEnd="verifyInputIsValid(event)">
<mx:columns>
<mx:DataGridColumn dataField="name" headerText="Name"
editable="false" />
<mx:DataGridColumn dataField="fieldNeedingValidation" editable="true"
id="fnv" headerText="Field Needing Validation" />
</mx:columns>
</mx:DataGrid>
当然,要使其工作,在退出verifyInputIsValid
函数之前,需要调用(并运行)remoteObjectValidationService的resultHandler
。以“同步”的方式。我知道,但必须有一个标准的方法来做这样的事情,对吗?我已经实现了我的自定义验证器,它工作得很好
在有效的客户端验证通过后,Flex程序员如何立即在服务器上进行验证?
我意识到寻找这种“同步”设计似乎很愚蠢,我希望有人能用最佳实践解决我的问题。在我的辩护中,我希望在客户端验证之后立即在服务器上进行验证的原因是,我正在使用Flex的验证框架。如果我从服务器得到无效响应,我希望利用Flex必须告诉用户他/她的输入有错误的内置UI组件
有什么想法吗?实现这一点的“同步”方法是首先进行服务器端验证。创建远程对象并执行服务器端验证:
private function verifyInputIsValid(event:DataGridEvent):void
{
var newValue:String = TextInput(evt.currentTarget.itemEditorInstance).text;
remoteObjectValidationService.addEventListener("result", function(event:ResultEvent):void{
resultHandler(event, evt);
});
remoteObjectValidationService.validate(newValue);
}
private function resultHandler(event:ResultEvent, evt:DataGridEvent):void{
//Check that the server-side validation is successful
if((event.result as String).toUpperCase() == "VALID"){
// Check the reason for the event.
if (event.reason == DataGridEventReason.CANCELLED)
{
return; // Do not update cell.
}
// For the fieldNeedingValidation only
if(event.dataField == "fieldNeedingValidation") {
// Get the new data value from the editor.
var newValue:String = TextInput(event.currentTarget.itemEditorInstance).text;
var validatorResult:ValidationResultEvent = myValidator.validate(newValue);
if(validatorResult.type==ValidationResultEvent.INVALID){
// Prevent the user from removing focus, and leave the cell editor open.
// Also, the edit will not continue and store the blank value
event.preventDefault();
// Write a message to the errorString property.
// This message appears when the user mouses over the editor.
TextInput(myPageGrid.itemEditorInstance).errorString = validatorResult.message;
return;
}
else if(validatorResult.type==ValidationResultEvent.VALID){
// Assuming the data is valid on the Server, this is fine
TextInput(myPageGrid.itemEditorInstance).errorString = "";
TextInput(myPageGrid.itemEditorInstance).text = newValue;
return;
}
}
}
}
服务器验证完成后,执行客户端验证:
private function verifyInputIsValid(event:DataGridEvent):void
{
var newValue:String = TextInput(evt.currentTarget.itemEditorInstance).text;
remoteObjectValidationService.addEventListener("result", function(event:ResultEvent):void{
resultHandler(event, evt);
});
remoteObjectValidationService.validate(newValue);
}
private function resultHandler(event:ResultEvent, evt:DataGridEvent):void{
//Check that the server-side validation is successful
if((event.result as String).toUpperCase() == "VALID"){
// Check the reason for the event.
if (event.reason == DataGridEventReason.CANCELLED)
{
return; // Do not update cell.
}
// For the fieldNeedingValidation only
if(event.dataField == "fieldNeedingValidation") {
// Get the new data value from the editor.
var newValue:String = TextInput(event.currentTarget.itemEditorInstance).text;
var validatorResult:ValidationResultEvent = myValidator.validate(newValue);
if(validatorResult.type==ValidationResultEvent.INVALID){
// Prevent the user from removing focus, and leave the cell editor open.
// Also, the edit will not continue and store the blank value
event.preventDefault();
// Write a message to the errorString property.
// This message appears when the user mouses over the editor.
TextInput(myPageGrid.itemEditorInstance).errorString = validatorResult.message;
return;
}
else if(validatorResult.type==ValidationResultEvent.VALID){
// Assuming the data is valid on the Server, this is fine
TextInput(myPageGrid.itemEditorInstance).errorString = "";
TextInput(myPageGrid.itemEditorInstance).text = newValue;
return;
}
}
}
}
您可以创建调用远程服务的自定义验证程序。在打电话之前,让验证器运行任何内部检查
我很久以前写过这样的东西。本可以做得更好,但你可以从中汲取一些想法
更新
下面是一个更清晰的例子,说明了这样的事情可能是什么样子
package
{
import flash.events.Event;
import mx.rpc.AsyncToken;
import mx.rpc.Responder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.validators.Validator;
public class UsernameValidator extends Validator
{
/**
*
*/
public function UsernameValidator()
{
super();
}
/**
* Inject or create some kind of service delegate to process the remote check.
*/
public var userNameService:IUserNameService;
/**
* Store the result of the remote check for the second pass through.
*/
private var _nameCheckResult:int = 0;
/**
* Overide this method to start the validation process
*/
override protected function doValidation(value:Object):Array
{
var userName:String = String(value);
var invalidChars:RegExp = /\W+/;
// Call base class doValidation().
var results:Array = super.doValidation(value);
// Return if there are errors.
if(results.length > 0)
return results;
// If input value is 0, or contains no value,
// issue a validation error.
if(!userName)
{
results.push(new ValidationResult(true, null, "required", "No user name was entered. Please select a user name"));
}
else if(userName.match(invalidChars))
{
results.push(new ValidationResult(true, null, "invalidChars", "This user name contains non alphanumeric characters [a-zA-Z0-9_]. Please select another and try again."));
}
else if(_nameCheckResult == 1)
{
//well assume that 1 means it's bad
results.push(new ValidationResult(true, null, "taken", "This user name has already been taken."));
}
else
{
//all checks have passed so return a special error type indicating a pending operation
//the string identifier is meaningless, except to indicate that it should be handled
//differencly by the consumer of the validation
results.push(new ValidationResult(true, null, "validating", "Checking username availability."));
//call some kind of remote service
var token:AsyncToken = this.userNameService.checkAvailability(userName);
token.addResponder(new Responder(userNameService_resultHandler, userNameService_faultHandler));
//...you should also add some logic to handle a change to the input if you want to use "live validation"
}
return results;
}
/**
* Dispatch some kind of event indicating an error (preferably with some return codes)
*/
private function userNameService_faultHandler(event:FaultEvent):void
{
trace("UserNameValidator.handleNameCheckError");
dispatchEvent(new Event("error"));
}
/**
* Check the result and dispatch an event indicating the validation needs to be run again.
*/
private function userNameService_resultHandler(event:ResultEvent):void
{
trace("userNameService_resultHandler(event)");
_nameCheckResult = event.result as int;
this.dispatchEvent(new Event("complete"));
}
}
}
package
{
import mx.rpc.AsyncToken;
/**
* The interface to a service delegate that checks
* the username and returns an AsyncToken.
*/
public interface IUserNameService
{
function checkAvailability(userName:String):AsyncToken
}
}
基本上是运行验证程序两次。当初始检查完成时,验证程序接收到异步操作的适当返回代码时,再次执行此操作。你如何做到这一点取决于你如何处理应用程序中的验证事件。这篇文章相当长,因此我认为“用解决方案回答我的问题”而不是“编辑””显示解决方案的问题是最合适的。我将对问题进行编辑,以更准确地反映问题的参数(即要求解决方案不包括任何额外的Flex框架,如or,以保持简单)
我也承认这个解决方案有点弱。目前,我有一个额外的事件触发,我需要找出并删除它-但它确实有效,并且符合业务/技术要求。这也让人觉得我是在“重新发明轮子”,我不想这样。因此,如果有人有一个例子(一些设计模式?),其中包括“客户端和服务器验证”,以及根据对项目编辑器的一些更改使用Flex验证框架(我的需要是一个),我将非常感谢您将其列在这里作为答案,也许我可以为您提供一些要点
我也不完全确定关闭/提交编辑器的方式。我确实尝试过使用destroyItemEditor()
,但它似乎对我不起作用
以下是我的源代码:
MyPage.mxml
UserRemoteObjectService.as
这就是当前的代码,@drkstr和@Kyle我想看看你的想法
谢谢StackOverflow,@drkstr您今天获得了“已接受”复选标记,是您启发了我的解决方案。Kyle,很抱歉,这不符合我的需要。我确信“最佳实践”是当且仅当在客户机上的验证成功时才在服务器上进行验证。你知道怎么强制吗?Brian,你可以重写我上面所做的,先做客户端验证,然后启动服务器验证。您只需要确保创建一个事件侦听器,将datagrid事件与服务器验证的结果事件一起传递。Kyle,对我的更新有什么想法吗?我想我已经知道了,但如果能有一些反馈就太棒了。@drkstr,我正试图将我的代码重构到您的设计中。我可以快速地问一下,您必须在UserNameValidator和使用它的任何组件/类中都有handleNameCheckError方法吗?相同的方法名,对吗?@drkstr还有,第三个代码块中myStatusText变量的意义是什么?实际上这是一个相当糟糕的例子,因为它是在我的应用程序中从特定用例的上下文中取出来的。我写这篇文章已经有几年了,但让我快速润色一下,我会发回的。@drkstr非常感谢您对这篇文章感兴趣,因为我尝试了许多解决方案,这篇文章耽搁了我两天;包括和。我现在正在考虑尝试解决这个问题。Cairngorm包括一个看起来很有前途的项目。我会让你不断更新!现在更新。这里有一些逻辑上的漏洞,但是如果你有困难的话,请告诉我。我可能还应该添加一个免责声明,有人会说这不是最好的解决方法。我个人可能会从验证器外部(例如,某种服务控制器)监听结果事件,然后对结果重新运行检查。验证程序仍然可以启动调用并保留自己的返回结果。但是,发布完整解决方案时+1:)我唯一的批评是服务代表。有分词吗
package validators {
import entities.IMyUser;
import entities.MyUser;
import events.MyValidatorEvent;
import flash.events.Event;
import mx.controls.TextInput;
import mx.controls.listClasses.IListItemRenderer;
import mx.events.DataGridEvent;
import mx.events.ValidationResultEvent;
import mx.rpc.AsyncToken;
import mx.rpc.Responder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.validators.ValidationResult;
import mx.validators.Validator;
import services.IUserService;
public class UserValidator extends Validator
{
public var userService:IUserService; //Service delegate to process the remote validation
private var _itemEditor:IListItemRenderer;
private var _dataGridEvent:DataGridEvent;
private var _inputValue:String = null;
public function UserValidator()
{
super();
}
/**
* The "Core Method" of this class. Invokes validation of a userIDToValidate
* and later takes the appropriate action on UI components as need be.
*/
public function validateSystemUser(userIDToValidate:String, itemEditor:IListItemRenderer ,dgEvent:DataGridEvent):void
{
this._dataGridEvent = dgEvent;
this._itemEditor = itemEditor;
var validatorResult:ValidationResultEvent = this.validate(userIDToValidate);
if(validatorResult.type==ValidationResultEvent.INVALID){
if(validatorResult.results[0].errorCode == "validating"){
// Prevent the user from removing focus, and leave the cell editor open.
// Also, the edit will not continue and store the blank value
dgEvent.preventDefault();
// Write a message to the errorString property.
// This message appears when the user mouses over the editor.
TextInput(itemEditor).errorString = validatorResult.message;
trace("Please wait, server is validating...");
return;
}
else{
// A client-side "invalid", handled the same. This time the message
// does not include "Please wait" text
dgEvent.preventDefault();
TextInput(itemEditor).errorString = validatorResult.message;
return;
}
}
else if(validatorResult.type==ValidationResultEvent.VALID){
// Everything was successful, update the UI
TextInput(itemEditor).errorString = "";
TextInput(itemEditor).text = userIDToValidate;
return;
}
}
// Overide this method to start the validation process
override protected function doValidation(value:Object):Array
{
if (_inputValue != String(value)){
_inputValue = String(value);
}
var results:Array = super.doValidation(value); // Call base class doValidation().
if(results.length > 0){
return results; // Return if there are errors.
}
//Business rules for client side validation will determine this
var someErrorCondition:Boolean = false;
if (someErrorCondition == true)
{
results.push(new ValidationResult(true, null, "errorCode", "Error description"));
return results;
}
else{
trace("All client-side validation has passed");
/**
* Call the remote service, return an 'error' indicating server validation
* is pending. The String identifier is meaningless, except to indicate
* that it should be handled differencly by the consumer of the validation.
*/
results.push(new ValidationResult(true, null, "validating",
"Please wait: \nThe server is validating this corpID."));
var token:AsyncToken = this.userService.service_findByID(_inputValue);
token.addResponder(new Responder(userValidator_resultHandler,
userValidator_faultHandler));
return results;
}
}
private function userValidator_resultHandler(re:ResultEvent):void
{
if(re.result.errorMessage == null)
{
var myUser:IMyUser = new MyUser(re.result.corpID,re.result.fullName,re.result.managerFullName);
var validatorCompleteEvent:Event = new MyValidatorEvent("totallyComplete", "", myUser);
this.dispatchEvent(validatorCompleteEvent);
}
else
{
trace("ERROR: Something went wrong in the userValidator_resultHandler");
}
}
/**
* This fault handler is invoked because my Server (via BlazeDS) actually
* returns/throws a custom Exception. This will dispatch an error to it's consumer
* (MyPage.mxml) using the details of that Exception/FaultEvent, used later to populate
* the same UI component as Flex's standard "Validator" (client-side) would.
* @see: http://livedocs.adobe.com/flex/3/html/help.html?content=validators_2.html
*/
private function userValidator_faultHandler(fe:FaultEvent):void
{
var myUser:IMyUser = new MyUser(this._inputValue,null,null);
var errorEvent:Event = new MyValidatorEvent("error", fe.fault.rootCause.message, myUser);
dispatchEvent(errorEvent);
}
public function get itemEditorInstance():IListItemRenderer
{
return _itemEditor;
}
public function get dataGridEvent():DataGridEvent
{
return _dataGridEvent;
}
}
}
package services
{
import mx.rpc.AsyncResponder;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.mxml.RemoteObject;
public class UserRemoteObjectService implements IUserService
{
private var _userService:RemoteObject;
public function UserRemoteObjectService(userService:RemoteObject)
{
this._userService = userService;
}
public function service_findByID(userID:String):AsyncToken
{
var token:AsyncToken = _userService.findById(userID);
token.addResponder(
new AsyncResponder(findByID_resultHandler,
findByID_faultHandler)
);
return token;
}
private function findByID_resultHandler(event:ResultEvent, token:AsyncToken=null):void
{
event.token.dispatchEvent(event);
}
private function findByID_faultHandler(event:FaultEvent, token:AsyncToken=null):void
{
event.token.dispatchEvent(event);
}
}
}