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

发布于 2024-11-05 09:33:29 字数 3442 浏览 0 评论 0原文

我是一名应届毕业生,请多多关照。我正在验证用户可以编辑的 Flex DataGrid 单元格中的输入。 DataGrid 中的行由 mx.collections.ArrayCollection 支持,其中包括我编写的 [Bindable]Model。我想针对 自定义客户端验证器< /a>,当且仅当通过时我想验证服务器上的输入。如果客户端验证失败,我想显示正常的验证错误(见下图)。如果服务器端验证失败,我想使用相同类型的 UI 组件来通知用户。该解决方案不应包含任何外部框架(CairngormPureMVC)。

标准验证器用法

我的 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>

当用户编辑单元格时,调用此函数:

private function verifyInputIsValid(event:DataGridEvent):void
{
    // 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;


            // I'd rather do this
            remoteObjectValidationService.validate(newValue);
            // Get a String result back from the call to the RemoteObject
            // Mark this "edit" (of the cell) as invalid, just as the client-side validator would

        }
    }
}

当然,为此要工作,在退出 verifyInputIsValid 函数之前,需要调用(并运行)remoteObjectValidationServiceresultHandler。以“同步”方式。我知道“Flex中的所有IO都是异步的”,但是必须有一个做这样的事情的标准方法对吗?我已经实现了我的自定义验证器并且工作得很好。

在有效的客户端验证通过后,Flex 程序员如何立即在服务器上进行验证?

我意识到寻找这种“同步”设计似乎很愚蠢,我希望有人有一个答案可以通过最佳实践解决我的问题。在我看来,我想在客户端验证之后立即在服务器上进行验证的原因是我正在使用 Flex 的验证框架。如果我从服务器收到无效响应,我想利用 Flex 必须告诉用户他/她的输入有误的内置 UI 组件。

有什么想法吗?

I'm a new grad, so please be kind. I'm working on validating input in a Flex DataGrid cell that the user can edit. The rows in the DataGrid are backed by an mx.collections.ArrayCollection that includes a [Bindable]Model that I wrote. I want to validate against a custom client-side Validator, if-and-only-if that passes I want to validate the input on the server. If client-side validation fails, I want to display the normal validation error (see image below). If server-side validation fails, I want to use the same sort of UI components to notify the user. The solution should not include any external framework (Cairngorm or PureMVC).

Standard Validator usage

My DataGrid implementation is:

<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>

When a user edit's a cell, this function is called:

private function verifyInputIsValid(event:DataGridEvent):void
{
    // 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;


            // I'd rather do this
            remoteObjectValidationService.validate(newValue);
            // Get a String result back from the call to the RemoteObject
            // Mark this "edit" (of the cell) as invalid, just as the client-side validator would

        }
    }
}

Of course, for this to work, the resultHandler of the remoteObjectValidationService would need to be invoked (and run) before we exit the verifyInputIsValid function. In a "synchronous" fashion. I know “All IO in Flex is asynchronous” , but there must be a standard way to do something like this right? I've already implemented my custom Validator and that works just fine.

How do Flex programmers validate on the server, immediately after the valid client-side validation passes?

I realize it seems silly to search out this "synchronous" design and I hope someone has an answer to solve my problem with best practices. In my defense, the reason I want to validate on the server immediately following client-side validation is so that I'm using Flex's validation framework. If I get an invalid response from the server, I want to leverage the built-in UI components that Flex has to tell the user something is incorrect about his/her input.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

鲜肉鲜肉永远不皱 2024-11-12 09:33:29

您可以创建调用远程服务的自定义验证器。只需让验证器在调用之前运行任何内部检查即可。

我很久以前写过这样的东西。它当然可以做得更好,但你也许可以从中汲取一些想法。

http://www.actionscript.org/forums/showthread.php3?t=173275

更新

这是一个更清晰的示例,展示了类似的内容。

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
    }
}

这个想法本质上是运行验证器两次。当初始检查完成时,然后当验证器收到异步操作的适当返回代码时再次检查。如何执行此操作取决于您在应用程序中处理验证事件的方式。

You can create a custom validator that calls a remote service. Just have the validator run any internal checks before making the call.

I wrote something like this a loooong time ago. It could certainly have been done better, but you may be able to pull a few ideas from it.

http://www.actionscript.org/forums/showthread.php3?t=173275

UPDATE

Here is a cleaner example of what something like this might look like.

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
    }
}

The idea is to essentially run the validater twice. Once when the initial check is done, then again when the validator as received an appropriate return code for the asynchronous operation. How you do this depends on how you are handling validation events in your app.

平定天下 2024-11-12 09:33:29

执行此操作的“同步”方法是首先进行服务器端验证。创建远程对象并执行服务器端验证:

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;
                    }
                }
            }
        }

A "synchronous" way to do this would be to do server-side validation first. Create a remote object and perform server-side validation:

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);
}

Once server validation is complete, perform the client-side validation:

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;
                    }
                }
            }
        }
温暖的光 2024-11-12 09:33:29

这篇文章很长,所以我想用解决方案“回答”我的问题,而不是“编辑”显示解决方案的问题是最合适的。我将编辑问题以更准确地反映问题的参数(即要求解决方案不包含任何其他 Flex 框架,例如 CairngormPureMVC >,以便保留 简单的)。

我也承认这个解决方案有点弱。目前,我需要弄清楚并删除一个额外的事件 - 它确实有效并且符合业务/技术要求。这也感觉好像我在“重新发明轮子”,但我宁愿不这样做。因此,如果有人有一个示例(某种设计模式?),其中包括“客户端和服务器验证”,以及对 itemEditor 进行某些更改时使用 Flex 验证框架(我需要的是 DataGrid 单元格编辑),如果您会在这里列出它作为答案,也许我可以给你一些分数!

我也不完全确定关闭/提交编辑器的方式。我确实尝试使用 destroyItemEditor(),但它似乎对我不起作用。

这是我的源代码:

MyPage.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
          xmlns:validators="validators.*"
          preinitialize="myPage_preinitializeHandler(event)"
          initialize="myPage_initializeHandler(event)"
          creationComplete="myPage_creationCompleteHandler(event)">

    <mx:Script>
        <![CDATA[
            import entities.MyModel;
            import entities.MyUser;

            import events.MyValidatorEvent;

            import mx.collections.ArrayCollection;
            import mx.controls.TextInput;
            import mx.events.DataGridEvent;
            import mx.events.DataGridEventReason;
            import mx.events.FlexEvent;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.remoting.mxml.RemoteObject;

            import services.UserRemoteObjectService;

            import validators.UserValidator;

            private var _userValidator:UserValidator;

            private var _securedPageService:RemoteObject;
            private var _securedUsersService:RemoteObject;
            private var _userRemoteObjectService:UserRemoteObjectService;

            [Bindable]
            private var _myModelList:ArrayCollection;

            protected function myPage_preinitializeHandler(event:FlexEvent):void
            {
                _userValidator = new UserValidator();
                _myModelList = new ArrayCollection();
            }

            protected function myPage_initializeHandler(event:FlexEvent):void
            {
                _securedPageService = new RemoteObject();
                _securedPageService.destination = "securedPageService";
                _securedPageService.getAllData.addEventListener("result",getAllData_resultHandler);

                _securedUsersService = new RemoteObject();
                _securedUsersService.destination = "securedUsersService";

                // For client-side and server-side validation using a RemoteObject service
                _userRemoteObjectService = new UserRemoteObjectService(_securedUsersService);
                _userValidator.userService = _userRemoteObjectService;
            }

            protected function myPage_creationCompleteHandler(event:FlexEvent):void
            {
                initializeModelList();
            }

            private function initializeModelList():void
            {
                _securedPageService.getAllData();   
            }

            private function getAllData_resultHandler(event:ResultEvent):void
            {
                var untypedList:ArrayCollection = (event.result as ArrayCollection);
                var myModel:MyModel;

                for each(var m:Object in untypedList)
                {
                    myModel = new MyModel(m.auditModelId, m.groupName,
                        m.reviewRequired, m.fieldNeedingValidation, m.lastReview)
                    _myModelList.addItem(myModel);
                }
            }

            private function verifyInputIsValid(dgEvent:DataGridEvent):void
            {               
                if (dgEvent.reason == DataGridEventReason.CANCELLED)
                {
                    return; // Edit is "cancelled", do not update
                }            

                // For the fieldNeedingValidation column only
                if(dgEvent.dataField == "fieldNeedingValidation") {
                    // Get the new data value from the editor.
                    var userID:String = TextInput(dgEvent.currentTarget.itemEditorInstance).text;

                    _userValidator.addEventListener("totallyComplete",userValidator_completeHandler);
                    _userValidator.addEventListener("error",userValidator_errorHandler);

                    _userValidator.validateSystemUser(userID, myPageGrid.itemEditorInstance, dgEvent);
                }
            }

            private function userValidator_completeHandler(event:MyValidatorEvent):void
            {
                TextInput(event.target.itemEditorInstance).errorString = "";
                event.target.dataGridEvent.itemRenderer.data.fieldNeedingValidation = (event.myUser as MyUser).fullName;
                myPageGrid.editedItemPosition = null;
                myPageGrid.selectedIndex = -1;
            }

            private function userValidator_errorHandler(event:MyValidatorEvent):void
            {
                // Prevent the user from removing focus,  and leave the cell editor open.
                // The edit will not continue and store the blank value
                (event.target.dataGridEvent as DataGridEvent).preventDefault();

                // Write a message to the errorString property.
                // This message appears when the user mouses over the editor.
                TextInput(event.target.itemEditorInstance).errorString = event.errorMessage;
                return;
            }
        ]]>
    </mx:Script>
    <mx:Panel title="My Page">
        <mx:DataGrid id="myPageGrid" dataProvider="{_myModelList}"
                     itemEditEnd="verifyInputIsValid(event)" editable="true">
            <mx:columns>
                <mx:DataGridColumn dataField="someField" headerText="Something" editable="false" />
                <mx:DataGridColumn dataField="fieldNeedingValidation" editable="true" headerText="Input User ID"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Panel>
</mx:Panel>

UserValidator.as

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;
        }
    }
}

UserRemoteObjectService.as

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);
        }
    }
}

这就是当前的代码,@drkstr 和 @Kyle 我很想知道您的想法。

感谢 StackOverflow,@drkstr 今天您获得了“已接受”复选标记,您启发了我的解决方案。

This post is quite long, so I thought "answering" my question with the solution rather than "editing" the question to show the solution was most appropriate. I will be editing the question to reflect the parameters of the problem more accurately (i.e. the requirement that the solution not include any additional Flex framework such as Cairngorm or PureMVC, so as to remain simple).

I also admit that this solution is a bit weak. At the moment, I have one extra event firing which I need to figure out and remove - but it does work and fit the business/technical requirements. This also felt as though I was "re-inventing the wheel" and I'd rather not. So if anyone has an example (some design pattern?) that includes "client and server validation", as well as using the Flex validation framework per some change to an itemEditor (my need is a DataGrid cell edit), I would really appreciate it if you would list it here as an answer and maybe I can offer you some points!

I'm also not entirely sure about the way that I'm closing / committing the editor. I did attempt to use destroyItemEditor(), but it didn't seem to work for me.

Here is my source code:

MyPage.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
          xmlns:validators="validators.*"
          preinitialize="myPage_preinitializeHandler(event)"
          initialize="myPage_initializeHandler(event)"
          creationComplete="myPage_creationCompleteHandler(event)">

    <mx:Script>
        <![CDATA[
            import entities.MyModel;
            import entities.MyUser;

            import events.MyValidatorEvent;

            import mx.collections.ArrayCollection;
            import mx.controls.TextInput;
            import mx.events.DataGridEvent;
            import mx.events.DataGridEventReason;
            import mx.events.FlexEvent;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.remoting.mxml.RemoteObject;

            import services.UserRemoteObjectService;

            import validators.UserValidator;

            private var _userValidator:UserValidator;

            private var _securedPageService:RemoteObject;
            private var _securedUsersService:RemoteObject;
            private var _userRemoteObjectService:UserRemoteObjectService;

            [Bindable]
            private var _myModelList:ArrayCollection;

            protected function myPage_preinitializeHandler(event:FlexEvent):void
            {
                _userValidator = new UserValidator();
                _myModelList = new ArrayCollection();
            }

            protected function myPage_initializeHandler(event:FlexEvent):void
            {
                _securedPageService = new RemoteObject();
                _securedPageService.destination = "securedPageService";
                _securedPageService.getAllData.addEventListener("result",getAllData_resultHandler);

                _securedUsersService = new RemoteObject();
                _securedUsersService.destination = "securedUsersService";

                // For client-side and server-side validation using a RemoteObject service
                _userRemoteObjectService = new UserRemoteObjectService(_securedUsersService);
                _userValidator.userService = _userRemoteObjectService;
            }

            protected function myPage_creationCompleteHandler(event:FlexEvent):void
            {
                initializeModelList();
            }

            private function initializeModelList():void
            {
                _securedPageService.getAllData();   
            }

            private function getAllData_resultHandler(event:ResultEvent):void
            {
                var untypedList:ArrayCollection = (event.result as ArrayCollection);
                var myModel:MyModel;

                for each(var m:Object in untypedList)
                {
                    myModel = new MyModel(m.auditModelId, m.groupName,
                        m.reviewRequired, m.fieldNeedingValidation, m.lastReview)
                    _myModelList.addItem(myModel);
                }
            }

            private function verifyInputIsValid(dgEvent:DataGridEvent):void
            {               
                if (dgEvent.reason == DataGridEventReason.CANCELLED)
                {
                    return; // Edit is "cancelled", do not update
                }            

                // For the fieldNeedingValidation column only
                if(dgEvent.dataField == "fieldNeedingValidation") {
                    // Get the new data value from the editor.
                    var userID:String = TextInput(dgEvent.currentTarget.itemEditorInstance).text;

                    _userValidator.addEventListener("totallyComplete",userValidator_completeHandler);
                    _userValidator.addEventListener("error",userValidator_errorHandler);

                    _userValidator.validateSystemUser(userID, myPageGrid.itemEditorInstance, dgEvent);
                }
            }

            private function userValidator_completeHandler(event:MyValidatorEvent):void
            {
                TextInput(event.target.itemEditorInstance).errorString = "";
                event.target.dataGridEvent.itemRenderer.data.fieldNeedingValidation = (event.myUser as MyUser).fullName;
                myPageGrid.editedItemPosition = null;
                myPageGrid.selectedIndex = -1;
            }

            private function userValidator_errorHandler(event:MyValidatorEvent):void
            {
                // Prevent the user from removing focus,  and leave the cell editor open.
                // The edit will not continue and store the blank value
                (event.target.dataGridEvent as DataGridEvent).preventDefault();

                // Write a message to the errorString property.
                // This message appears when the user mouses over the editor.
                TextInput(event.target.itemEditorInstance).errorString = event.errorMessage;
                return;
            }
        ]]>
    </mx:Script>
    <mx:Panel title="My Page">
        <mx:DataGrid id="myPageGrid" dataProvider="{_myModelList}"
                     itemEditEnd="verifyInputIsValid(event)" editable="true">
            <mx:columns>
                <mx:DataGridColumn dataField="someField" headerText="Something" editable="false" />
                <mx:DataGridColumn dataField="fieldNeedingValidation" editable="true" headerText="Input User ID"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Panel>
</mx:Panel>

UserValidator.as

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;
        }
    }
}

UserRemoteObjectService.as

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);
        }
    }
}

So that's the current code, @drkstr and @Kyle I'm interested to see what you think.

Thanks StackOverflow, @drkstr you're getting the "Accepted" check mark today, you inspired my solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文