Reputation: 1130
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:
But as you see no one is really reasonable. Is there really no simple solution to the problem?
Upvotes: 12
Views: 3279
Reputation: 3618
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()
.
true
at least one required field is not filled : I uncheck the flag completed
. (it is a draft)false
all the form is completed : I check the flag completed
. (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 :
NOT NULL
constraints.NULL
values. outputLabel
for your fields.Upvotes: 1
Reputation: 1109695
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.
Upvotes: 8