4 Replies Latest reply on Sep 10, 2010 4:02 AM by ilya_shaikovsky

    client validation framework

    ilya_shaikovsky

      This thread is for discussion of implementation questions for RichFaces Client Validation mechanism.

       

      Base document for discussion - http://community.jboss.org/wiki/ClientSideValidation

       

      In order to start I will make write-up based on Pavel prototyping work and our local discussion of some problems with it.

       

      First point we discussed - way to provide form validation. It should be done in order to handle form submits properly. Consider next case -

       

      <form>
      //set of inputs with validators attached
           <input/>
      
           <input/>
      
           <input/>
      
           ...
      //store button
           <button>
      
      </form>
      

       

      inputs itself could be validated on blur or keyup or some other event.. But after clicking the button - we should be able to invoke the same validation and to cancel submit if any errors occured. So we coming with proposal (already written in comments of base document) to the usage of clientValidator tag not only for EditableValueHolders but also for form or even view like:

       

      <form>
           <rich:clientValidator [event="onsubmit"]/>
           ....set of inputs there
      

       

      So inputs in form could even omit clientValidator definition, if UI requirements do not consist of highlighting wrong inputs immediatelly but only after store button pressed. Using such declaration - validators should be attached by tag handler to all the inputs in form but called only by the script handler of form onsubmit. And form submit will be canceled by stopping the event in validators if some problems reported.

       

      Second point is - implementation of clientValidator as a behavior looks not really convinient for us. Instead we propose to register validators by using some CV api like there:

       

      <input>
           <clientValidator />
      </input>
      

       

      should generate input and some js code to bind validators to it:

       

      addValidator(id, options)

       

      where options is some array of objects like

       

      {event, validator:function, validatorOptions, validationMessages:['statusId':message, ...], msgComponents:[...]}
      //all that stuff generated according to Bean validation annotations 

       

      instead of encoding like onchange = "validators calling JS".

       

      Main advantages of such approach:

      1) if we adding validation to whole form - we could just call that registered validators easilly from the handler attached to the form. And if this will be a behavior - code duplication will appears in form component onsubmit in order to register all the same validation methods.

      2) possibility to register validator without event specifying (not possible with behavior but also required for form validation case(no validation untill submit pressed) described in first point)

       

      Example of registration form validator using not behavior but handler registration:

      addFormValidator(formId)
      // the form validator validate method - will lookup for registered validators inside all the nested inputs
      

       

      Third point although the lookup mechanism for validators JS implementation looks really good and allows to avoid any registry creation and having mappings for Bean validator - > client validator - it has one problem. We definitelly need to aggregate validators implementations required for concrete view. As Alex written in initial document there could be too much resources requests in other case and even the problems with browser restrictions of count of such inclusions could appear.

       

      so this point looks required to be implemented:

       

      Define JavaScript resource and function name in the faces-config.xml as extension element. Allows to use one .js file for many converters/validators and define short names for functions. Disadvantage: complicated configuration.

       

      Open question - do we able to add some runtime aggregation in order custom validators implementations also be included as bundled into single resourse? Does it sounds reasonable from performance point of view for example?

       

      Fourth point Ajax call for validation if no JS implementation exist or no problems found during JS validation(also seems reasonable). In our opinion this should became at least configurable at tag level as Emmanuel mentioned.

       

      Example: If most of the form inputs has simple js Validators attached and the single input needs to be additionally validated at server side(e.g. checking email or nickname duplication during registration requires server side validator) - it's not looks reasonable to flood server with many validation requests but perform them only where them really required for that concrete fields. 

       

      So could look like:

      <input>
           <clientValidator useAjaxValidation="true"/>
      </input>
      

       

      Fifth Point We propose to add the handling of <rich:messages/> also in next way: If the rich:messages with globalOnly="false" exist - it should be also registered and used by some RichFaces.validators.addMessage() api during addition of validation message to target messages components. We just need to use clientId's of inputs which causes validation message in order to identify the messages added there and perform proper clean-up, update after further changes in form and next validations.

        • 1. Re: client validation framework
          ilya_shaikovsky

          high level proposal of JS implementation:

           

          definition of the behavior on the component with converters/validators attached on the page:


          <input value="{bean.value}" id="input"> 
               <f:converter type="org.demo.converter.CustomConverter"> 
          </input> 
          <r:message for="input">

           

          and Bean:

          import org.demo.validator.CustomValidator; 
          class Bean 
          
              @CustomValidator 
              private int value
          

           

          So according to look-up algorithm described by Alex we should look-up resource for validator at org.demo.validator.customValidator.js and converter at org.demo.coonverter.customConverter.js.

           

          org.demo.validator.customValidator.js code:

           

          (function ($,rf) { 
              var function1, function2; 
           
              return function (value, params) { 
                  return message; 
              } 
          })(jQuery, RichFaces);

          The code is just example. It should be function or should return function which performs validation. The same for converter.

           

          org.demo.validator.customConverter.js code

           

          (function ($,rf) { 
              var defaultOptions = { 
                  minChars:10; 
              }; 
              var function1, function2; 
          
              return function (value, params) { 
                  return Object; //Object contains of converted value or error message 
                                 //and flag if the conversion was successfull  TODO- revise!!!
              } 
          })(jQuery, RichFaces);
          

           

          Validators and converters should be aggregated to JS resources like:

           

          Converters:

          rf.csv.addConverter({'customConverter':(function ($,rf) { 
                  var defaultOptions = { 
                      minChars:10; 
                  }; 
                  var function1, function2; 
           
                  return function (value, params) { 
                      return messageString; 
                  } 
              })(jQuery, RichFaces);, 
           ...
           });

           

          and Validators:

           

          rf.csv.addValidator({'customValidator':(function ($,rf) { 
                  var function1, function2; 
           
                  return function (value, params) { 
                      return [code]; 
                  } 
              })(jQuery, RichFaces);, 
           ...
          });

           

          All the definitions above described the js resources which contains complete bundles with validators and converters.

           

          And in order to register validators on the concrete form next script should be added for every form registering all the behaviors present inside:

           

          rf.csv.addFormValidators( formId, {inputId||componentId: function  (event, this||id){ 
                  rf.csv.validate(event, [this||id],  {'customValidator1':{options hash},'customValidator2...}, messageIds[], options {converter:, 
           
          pattern:, ...}) 
              }, 
              ... 
          });

           

          And actual js call which should be encoded to component event handler:

           

          onchange="rf.csv.v(this||id);"



          rf.csv.v function finds validate function registered with addFormValidators for this id and call it.

           

           

          As decided - we should register whole form validation at command components so the behavior placed to UICommand will be encoded as:

           

          rf.csv.vf(this||formId);

           

          rf.csv.vf function finds validate functions for the form and call them.

           

          Messages API:


          Initial messages loading to the client.


          Them should be agregated simillary to js resources:

          rf.csv.addMessage(messages): 
              messages - hash array with messagea as key, value: 
              key: messageId 
              value: message pattern (for example: 'Value {1} should be less than  {2}' ) 
           

           

          How to get concrete message to add to messages component:

           

          rf.csv.getMessage(messageId);

           

           

          TODO: messages client components and API.

          • 3. Re: client validation framework
            jbalunas
            <input value="{bean.value}" id="input"> 
                 <f:converter type="org.demo.converter.CustomConverter"> 
            </input> 

            We need to avoid requiring the user to specific the type of validator/converter required for each field.  That is the primary point of Bean validation - you don't need to specific details in different layers. 

             

            • All standard converters/validators should be automatically applied based on inspection.
            • Custom validators/converters need to be registered by the creator ( xml, property file, etc...)
              • Part of converters ( custom, or standard ) should be defining default class that the converter will be applied to, just like JSF 2:

            forClass = ZipCode.class

            • 4. Re: client validation framework
              ilya_shaikovsky

              agree with all your points. But I guess it's not adds much complexity if we will discover not only validators made by adding annotations but also ones which added on the view. Just sample shows that we should be able to discover converters defined in that way also as them defined with fully cvalified class names(or registered in configs). And there are seems not much problems to look-up the same resources impl for client side in similar resources packages.

               

              Alex?