Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/flash/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/apache-flex/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Flash 使用Flex 3处理客户端和服务器验证?_Flash_Apache Flex_Actionscript 3_Design Patterns_Flex3 - Fatal编程技术网

Flash 使用Flex 3处理客户端和服务器验证?

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

我是刚毕业的,所以请友善一点。我正在验证用户可以编辑的FlexDataGrid单元格中的输入。DataGrid中的行由一个
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);
        }
    }
}