Client Side Validation

Usage

RichFaces client-side validation should be implemented as client behavior that produces script for validation procedure. That behavior should work with components that implement EditebleValueHolder interface :

<h:inputText id="input" value="#{foo.bar} ......>
   <rich:clientValidator [event="onchange"] ... >
</h:inputText> 
<rich:message for="input" ..../>

The behavior should perform validation on the client side, if possible, or call server-side code by ajax otherwise, so application developer should not care which validator is compatible with RichFaces framework..

 

Update IS. and PY.

After discussion with Alex S. decided that form level validation - should be added by attaching the behavior to corresponding UI Command. The reason - addition to form is problematic because some UICommand's in form could be "Cancel" buttons, some controls which changes just form representation and so on. And in those cases - validation should not be performed. So it's easier to use

<h:commandButton id="save" value="Store! ......>

   <rich:clientValidator [event="onclick"] ... >

<h:commandButton>

for the ui command's which should trigger validation prior to submission.  

So the requirement should be changed in order to add information that CSV behavior should work with UICommand's and EditableValueHolder components.

Implementation:

The getScript() method of such behavior should return JavaScript code that calls validation method, generated by framework :

<input .... onchange="form_input_v(clientId,this[, event])". Framework generates function definition at the end of current form or page body

where:

  • Component clientId is used to find input value and dispatch error messages. For complex component, it would be used to extract input value.
  • this - reference to html input element or JavaScript object that performs event call. Validator expects client input string as 'value' attribute of the such object.
  • event - optional DOM event object.

Server side code:

  1. ClientValidator behavior checks input parameters and whether target component implements EditableValueHolder interface. If it is, follow further steps. Otherwise, check target component for ActionSource interface to call form validation script. For all other types throw exception. See note in usage.
  2. Optional: in the development stage, behavior should look for the message component associated with target input. If no such component defined on the page, only warning log message recorded because there is no way to display error message on the client.
  3. determine JSF converter associated with target input component. If appropriate converter found, lookup JavaScript library that contains client-side equivalent of that converter ( see below how to define such association ) . Real client-side validation should be performed only if JavaScript converter code available; framework should use AJAX otherwise.
  4. Determine all validators associated with target component by JSF attributes and as Bean Validation annotations. For each validator, lookup associated JavaScript function ( same as for converter ). If such function not found for at least one validator, AJAX call should be performed as the last step of validation procedure. If no one Validator was set for input component, return empty String or null.
  5. Calculate message strings for each validator that has client-side script version using current view locale. For Bean Validation, determine client-side MessageInterpolator. If it is not possible to calculate message string or interpolator, only AJAX call should be performed.
  6. Put information which is necessary to generate validation function to special ViewResource component that will generate script elements to load necessary libararies and function bodies.
  7. Returns JavaScript code to call validator function.
  8. ViewResource component generates per-component validation functions, and form-wide validator function which calls all per-field validators. It also generates <script> elements for all resources used by validators and creates MessagesDispatcher.

Code snippet from Emmanuel:

 

@Inject Validator validator;
 
void doStuff() {
  Set<ConstraintDescriptor<?>> constraints = validator
    .getConstraintsForClass(Address.class)
      .getConstraintsForProperty("street1")
        .findConstraints()
          .unorderedAndMatchingGroups(Default.class) //or the requested list of groups)
          .getConstraintDescriptors();
 
  //ContextHolder is an arbitrary object, it will depend on the implementation
  processConstraints( constraints, new ContextHolder() );
}
 
void processConstraints(Set<ConstraintDescriptor<?>> constraints, ContextHolder holder) {
  for( ConstraintDescriptor cd : constraints ) {
    Annotation a = cd.getAnnotation();
    Map<String, Object> parameters = cd.getAttributes();
    //TODO if cd.isReportedAsSingleConstraint() make sure than only the root constraint raises an error message if one or several of the composing constraints are invalid)
    holder.pushState(); //or something like that 
    processConstraints(cd.getComposingConstraints(), holder); //process the composing constraints
  }
}

 

  1. register all necessary JavaScript resources, build JavaScript function call.

Client-side sequence:

  1. convert input string using converter function, if provided. If converter throws exception, update message as described below and stop.
  2. call all validator functions one by one. If there is an exception, update message component with that message and stop.
  3. when no errors were found on steps 1 and 2, perform AJAX call if required or clear message content otherwise.

Process  form submit component:

  1. Call validation functions for all components registered during server-side rendering.
  2. if any error occurs during validation, display messages for them and return 'false', that prevents form submission.
  3. Otherwise, does nothing and let request to be processed on the server.

Message updates:

  • Manipulate content of html element renderer by message component directly believe to Html render kit specification.
  • rich:message should register itself in MessageDispatcher  to update its content by JavaScript event. Message widget should receive messages for same clientId what was calculated from "for" attribute value during render phase.

AJAX validation:

If no client-side script exists for the all validators and converter, client-validator behavior should perform AJAX call that executed on the target input component only, and updates only message components associated with it. Behavior should set FacesContext#renderResponse flaf after the PROCESS_VALIDATORS phase ( or APPLY_REQUEST_VALUES for immediate components ), even if there was no validation error, to prevent unexpected model update.

JavaScript functions lookup:

There are couple of possible ways to determine appropriate function name:

  • Lookup for JSF 2.0 resource in the special library with the same ID as the server-side Java class name. The name of that function should also contain some namespace to differentiate. It is easiest method that does not require any additional configuration. The disadvantage in the long names that would come from a deep package structure and significantly increase HTML code size. Also, each component requires its own JavaScript file that increases number of http requests.
  • 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.
  • Special annotation for JSF objects or Bean Validation constrains. Applicable for such validators only, not applicable for already existed constraints.

Combination of the first two methods seems the most convenient. faces-config can be used by library developers while name convention would be more useful for application.