For some time ago, we had decided not to use JSF
’s required
attribute and instead handle required validation with a
validator
object implementing Validator
interface, attached to the current UIInput
component. By that way, we were
able to develop validation bypass mechanism while being able to update the model
, and group validation capability which
let us group components in the same UIForm
and trigger their validators
separately. My
ex-teammate has actually created
an open source project
and made those works public, so that other poor JSF
developers having nightmares with JSF
’s
silliness could benefit from them.
Actually, our work has been working without any problem till I recently decided to develop a bulk update mechanism with
an editable datatable
component. When I placed some UIInput
components to display column values and at the same time
let users edit those column values, somehow required validation errors started to show off on my screen, although
non-empty/non-null values for those components were being submitted with the current request.
After a quick investigation I realized that UIData
is handling decode
, validation
, update model
phases differently
than other UIInput
components on the same page. Shortly speaking, UIData
component iterates over each row and calls
processDecodes
, processValidators
and processUpdates
methods of each and every child UIComponent
. Components
submittedValues
are set during those calls, but at the end they are reset again. In other words, those child components
will have their localValue
and submittedValue
properties set only during processXXX
method calls of their ancestor
UIData
component.
On the other hand, our required validation fix
was working as follows. First it starts from UIForm
or UIViewRoot
and
traverses the whole component tree and tries to find components with EditableValueHolder
interface. When a component
is found with that type, its registered validator
objects are checked if one for required validation is available. If
it is, then that required validator
is called either with localValue
or submittedValue
of that component.
Unfortunately, EditableValueHolder
components with a UIData
ancestor will contain none of them as explained above.
As a result, required validation fails while not-null/not-empty component values exist in the current request.
It looked that our fix was limited to components outside UIData
components. After a deep thought, I decided to return
back to use of JSF
’s required
attribute. There was no other way around. However, I needed a way to keep those bypass
and group validation capabilities available in our project. My solution is simply based on disabling and enabling required
attributes of those components during processDecodes
, processValidators
method calls. First part traverses the whole
component tree and calls setRequired(false)
of components in case validation is bypassed or the current component
hasn’t a validator
with groupId
equals to active validation group id submitted. After that processXXX
methods are
executed. If any component left with required=true
, its value will be checked by JSF
. If validations are asked to be
bypassed, or current active validation group id is different than registered validators
’s groupId
attributes, then
those components’ required
checks won’t happen. At the end, second part of the solution starts executing. It again
traverses the whole component tree and restores original required
attribute values of those components.
In summary, I am able to keep available bypass and group validation capabilities, while having components inside UIData
validated correctly. On the other hand, I again exposed to JSF
specification’s bad design choices again. I don’t know
if the expert team had considered required validation to be handled the same as other validators
, but if they had so,
JSF
’s validation framework would have been much more consistent, and would be giving more options to customize the
mechanism similar to our ones.