JSF how to temporary disable validators to save draft
I have a pretty complex form with lots of inputs and validators. For the user it takes pretty long time (even over an hour) to complete that, so they would like to be able to save the draft data, even if it violates rules like mandatory fields being not typed in.
I believe this problem is common to many web applications, but can't find any well recognised pattern how this should be implemented. Can you please advise how to achieve that?
For now I can see the following options:
- use of immediate=true on "Save draft" button doesn't work, as the UI data would not be stored on the bean, so I wouldn't be able to access it. Technically I could find the data in UI component tree, but traversing that doesn't seem to be a good idea.
- remove all the fields validation from the page and validate the data programmaticaly in the action listener defined for the form. Again, not a good idea, form is really complex, there are plenty of fields so validation implemented this wa开发者_C百科y would be very messy.
- implement my own validators, that would be controlled by some request attribute, which would be set for standard form submission (with full validation expected) and would be unset for "save as draft" submission (when validation should be skipped). Again, not a good solution, I would need to provide my own wrappers for all validators I am using.
But as you see no one is really reasonable. Is there really no simple solution to the problem?
It's indeed not that easy. Validation is pretty tight coupled in JSF lifecycle.
I would personally go for option 1. True, dirty work, but you can just hide that away in an utility class or so. Just grab the <h:form>
in question from the viewroot, iterate over its children recursively, hereby testing if component instanceof EditableValueHolder
is true, store the found id-value pair in sort of Map
and finally persist it.
As a fourth alternative, you could save all the data independently using ajaxical powers. jQuery is helpful in this.
$.post('/savedraft', $('#formid').serialize());
It only requires Javascript support at the client side.
Update: the JSF utility library OmniFaces has a <o:ignoreValidationFailed>
taghandler for the exact purpose. It was indeed not a simple solution as it requires a custom <h:form>
as well. It does its job by providing a custom FacesContext
instance during the validations and update model values phases which does a NOOP in the validationFailed()
and renderResponse()
methods. So the components are still invalidated and the messages are still attached, but it would still proceed to the update model values and invoke application phases.
I had the same problem and I didn't like the idea of skipping all the validations. After a lot of thought I ended up wanting only to skip required fields validation. The logic behind this is the user either complete a field correctly or doesn't complete it at all. This is very important for me because everything ends up in the database and, of course, I don't want to overflow a database field or end up saving a String
value into an INT
database field for instance.
In my experience, skipping required fields allows enough margin of manoeuvre to save a draft. To achieve that I ended up writing a requiredWarnValidator
that shows up a single warn message.
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
if (value == null) {
FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_WARN);
message.setSummary("This field is required.");
context.addMessage(component.getClientId(), message);
context.validationFailed();
}
}
In this validator I do not throw a ValidatorException()
because I want to pass the validation phase but I call validationFailed()
because I want to know if a required field is not filled.
I have a flag (completed
) in the entity I use to save my form. When saving the form, I check isValidationFailed()
.
- if
true
at least one required field is not filled : I uncheck the flagcompleted
. (it is a draft) - if
false
all the form is completed : I check the flagcompleted
. (it is not a draft)
This also allows me to have a single "Save" button instead of two buttons ("Save" and "Save as a draft").
Notes and known pitfalls :
- If you are saving your draft to the database then you have to make sure there are no
NOT NULL
constraints. - When using converters and validators you have to make sure they can handle
NULL
values. - You will lose the required field asterisk in the
outputLabel
for your fields.
精彩评论