Client-side validation done wrong (with Spring-Faces)

In JSF there is no concept of client-side conversion and validation. All is done on the server, which means you notice some extra round trips in case there is something “wrong”. Eventually JSF 2.0 will change that…

In the meantime there are some frameworks that provide help. One of them is Apache MyFaces Trinidad.

The basic idea of Apache Trinidad client conversion and validation is that it works on the client in a very similar way to how it works on the server, except the language on the client is javascript instead of java. There are javascript converter objects that support the methods getAsString() and getAsObject(). A TrConverter can throw a TrConverterException. There are javascript validator objects that support the validate() method. A TrValidator can throw a TrValidatorException. Read here for more details on how to decorate your standard converter/validator to enable the client-side representation.

Another possibility is using Spring-Faces, which I think is quite popular since a lot of folks use the WebFlow stuff. But I don’t really like their way of *enabling* client-side conversion / validation.

Their user guide gives some interesting examples on how to use the bits, like:

<sf:clientTextValidator required="true">
  <h:inputText id="creditCardName"
  value="#{booking.creditCardName}" required="true"/>
</sf:clientTextValidator>

What this does is basically enabling client-side “required” validation.

Cool, but noticed it? You add things to your declarative view, that you eventually don’t really want…

  • you handle client-validation as a special case (some extra tag for that used here)
  • you wrap the “validator” around the actual component (JSF usually nests real validators, but this is “just” a dojo wrapper component (to include some fancy dojo-based JS)
  • you are redundant (required true on the client AND on the server (see the <h:inputText />
  • you can (by accident) disable server-side validation, by just saying required:true on the “validator” tag. Think about a man-in-the-middle tool (not tested this)

Another example I saw here:

<sf:clientTextValidator required="true" regExp="[0-9]{16}"
invalidMessage="A 16-digit credit card number is required.">
  <h:inputText id="creditCard" value="#{booking.creditCard}" required="true"/>
</sf:clientTextValidator>

This does validate the entered value to match a regular expression. In Trinidad, this would be coded like this:

<tr:inputText id="creditCard" value="#{booking.creditCard}" required="true"/>
<tr:validateRegExp pattern="[0-9]{16}" messageDetailNoMatch="A 16-digit credit card number is required."/>
</tr:inputText>

This is the way JSF users know how to compose the views. Also, a very minor thing… In Trinidad all attributes to change the messages start with message. In Spring-Faces not. They are called like “promptMessage” or “rangeMessage”. Of course pretty minor, but useful for users that type the XHTML/JSPX in a tool and don’t know the exact naming… you just need to remember messageXzy… ;-)

Of course, there is no standard way of doing things like conversion/validation on the client, which explains why there are different solutions. In Trinidad we actually render the components and we are also in control of the extra validators. So, we can do some stuff inside of our library for things like:

<inputText required="true" ... />

I really hope to see some client-side conversion / validation in JSF 2.0; Looking at Spring-Faces (compared to Trinidad) show that a common solution is really needed. And this solution – I am pretty sure – will follow the natural JSF way, like Trinidad does today.

About these ads

Howdy!

Posted in java, javascript, jsf, myfaces, spring, spring-faces, trinidad, web²
5 comments on “Client-side validation done wrong (with Spring-Faces)
  1. Steve H. says:

    I agree Matthias- this seems a little cludgy. I think of validation as an aspect of an input, therefore it BELONGS to the input, hence it should be nested inside the tag (like Trinidad). Wrapping the thing in a validator doesn’t feel natural. The Spring-Faces approach seems contrived.

  2. Matthias,

    I appreciate you taking a look at our approach and sharing your perspective. Perhaps I can offer some additional insight as to why we chose to implement things this way.

    First off, these decorator style components are meant to be generic and applicable to any sort of input component, so we cannot rely in any way on how the rendering process is implemented by the component being decorated. This is why the outer-tag approach was used, in order to ensure that the original component has completely rendered its output. These decorators are meant to be unobtrusive. They progressively add client-side behavior and thus do not modify the server-side behavior of the decorated component in any way.

    We show setting required=”true” on both the client and the server in order to reinforce the idea that there should be sufficient server-side validation in place to be robust in the face of JavaScript failing or being completely unavailable on the client. You will see the philosophy of progressive enhancement is central to the components we provide in Spring Faces, ensuring that most everything (including our commandLink component) will still be completely functional if JavaScript is unavailable. We could have chosen to propagate the required property setting onto the decorated component, but I hate to force such things onto the user, especially given how problematic JSF’s server-side required validation can be on a complex page. Often our users are better off implementing the required check using the model-level validation facilities we provide in Web Flow.

    As for the naming of the message attributes, those directly mirror the underlying Dojo widget attributes. We intentionally chose not to hide the fact that Dojo is being used underneath, and thus did not want to use different names for the attributes. In the future, I’d actually like to make the connection to Dojo even more transparent by providing a more generic declarative tag that would allow the user to apply any Dojo widget as a decoration. (In other words, a declarative version of the Spring.ElementDecoration API from Spring JavaScript.) Where I think all of this will really start to become more useful is when JSF 2.0 is out and makes it easier to create template-based components. My hope is that these declarative decorators will serve as simple building blocks to make it easier to quickly build composite components with rich client-side functionality.

    That said, I do agree that it would be nice to see client-side conversion and validation standardized in JSF 2.0, and I admit that I would expect it to look more like what you’ve done in Trinidad since it would be incorporated into the standard renderkit. The EG has been considering the idea of being able to automatically add both client-side and server-side validators based on JSR-303 metadata in the model, and I think that would be a great solution if done right.

    Cheers,

    Jeremy Grelle

  3. matthiaswessendorf says:

    Jeremy,

    thanks for taking the time to respond.

    You said:
    We show setting required=”true” on both the client and the server in order to reinforce the idea that there should be sufficient server-side validation in place to be robust in the face of JavaScript failing or being completely unavailable on the client.

    In Trinidad, you just set it on the component (yes, we are in charge of the rendering, I understand that) and if client-side stuff is active (you can disable client-side validation via trinidad-config.xml) it is done on the client AND on the server. If there is no client support you can’t disable the real server-side validation model, when said required:true.

    In Spring-Faces this is unfortunately true (same in Shale, which also has a “bad” design for the extra validators).

    I’d be more than happy if there is a nice integration of 303 / client-side stuff as well.

    Even if JSF2 does not contain client-side conversion/validation it should be easier to provide a better integration of client-side stuff, by leveraging the BeforeRenderEvent.

    -Matthias

  4. Matthias,

    “In Trinidad, you just set it on the component (yes, we are in charge of the rendering, I understand that) and if client-side stuff is active (you can disable client-side validation via trinidad-config.xml) it is done on the client AND on the server. If there is no client support you can’t disable the real server-side validation model, when said required:true.”

    Right, but it seems you missed part of my point. What if you have a complex page with numerous in-page events where you do not yet need to enforce that field to be required? In other words, you only want the field to be required if the user signals a particular event (such as a “save” event). The way our client-side validation works in Spring Faces, you usually prevent server-side events from being signaled (as a result of the client-side validation not passing) only for certain buttons/links. That way you can allow other more fine-grained events that mutate the current view in some way to propagate to the server when the validation does not matter. With JSF’s server-side required validation, there is no way to say, “this is required only for this particular event” and people end up resorting to workarounds such as immediate=”true”. Here it is useful to be able to move the required checking into model-level validation that can be fired for a particular event.

    -Jeremy

  5. matthiaswessendorf says:

    I haven’t said the JSF validation model is the best in the world… But I am not sure if treating this as a special case is right as well.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 33 other followers

%d bloggers like this: