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 way 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 flag completed. (it is a draft)
if 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 :
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.
Related
To not have to keep repeating some validations, for example, who can see a button in a certain status of a document in the worlflow, I'm using session, scope, and session variables to store the user roles and application variable to store the Status related to each area.
I was evaluating whether it would be better from a performance and build point of view to implement a managed bean, to return the user roles and the possible statuses of each participating workflow area. Would it be the best structure in fact? What do you think? I do not have much experience in java. How could I construct the structure in java, several methods, one for roles and the other for set of status associated with the area that would name the related method? You could return the results of this method in arrays, or there is a better return structure.
Thanks a lot!
My best suggestion is to adopt the pageController Methodology. Then it's more like true MVC. This has been talked about on NotesIn9 screencast many times but basically you have a java object that's bound to your XPage. In effect it's a viewScoped bean that holds all your page logic. Then you can have methods like isGroupMember(), hasRole() etc and calculate that on the pageInit. There's little need to hold onto that in sessionScope in my opinion. So for example I have this in my pageController :
public boolean isGroupMember(String groupName) {
return JSFUtil.getXSPContext().getUser().getGroups().contains(groupName);
}
So that's available to each page. BUT I don't need to copy that snippet onto every page controller. In Java you can have your page controllers extend a more generic class. so I have a "base.pageController" class. All the specific page controllers extend that. So this isGroupMember() code goes into the base and then it's available to be used on every XPage. Doing it this way gives you the ability to have generic functions like this and then hold more specific function that are only for the individual page.
You can also have a hasRole() function etc...
Recommend you check out this video : http://www.notesin9.com/2016/08/25/notesin9-196-no-dependency-page-controllers/
Also for a question like this, I recommend you just use the xpages tag. Adding others like javabeans can bring people in who know nothing about XPages and XPages is unique enough of a beast that outsiders can cause some confusion on occasion.
I've written a new back-end Java component (extending UIComponentBase) as an alternative for the ExtLib Application Layout control. The control needs to show a collection of data that is looked up from another Notes application. The data is user dependant and doesn't change from page to page so, to avoid repeatedly doing a lookup to the other application, I want to store it in the session scope. (Note that because this is a layout control, there will only ever be one per page.)
I know I could use a session-scoped maanged bean (and have done in previous iterations) but the data only needs to be used in this control and shouldn't be used elsewhere on the page which it could be with a bean. So my question is, what's the best practice approach I should take here? Should I just directly store the data in the sessionMap or am I missing a trick with the component stateHolder? Or am I stuck with using a bean?
Thanks
(Edited for clarification)
It looks like you're talking about your own back-end Java components rather than Custom Controls within a single NSF.
I'm not sure at what level, when you write your own native XPages components, the properties are cached by the stateHolder when calling saveState(). I would presume no higher than View, for the reasons Frantisek says, that otherwise it would be unclear which instance to update if you had multiple on one XPage but one on another. It couldn't update both at the same time on the same page, so I would guess that each is a separate instance. As a result, the same component on multiple pages would be a separate discreet instance.
But there's nothing stopping you, in specific setters of the component, writing to sessionScope as well as the private property, and then doing the reverse on the getter. I'm not sure if you'd want to try the internal property before trying sessionScope or vice versa. It would depend how you wanted to handle the situation of the same sessionScope being updated from multiple pages (if the collection could change).
Problem description
At the company I work for we're implementing a web application using JSF2 and PrimeFaces. The web app is one of the front-ends hitting a bunch of business methods and a domain model created as a set of entity classes and persisted using JPA. We also have a couple of webservice methods operating on that same domain model. Because of this we want to implement as much of our validation logic as possible using bean validation on the entity level. So far everything's working out quite nice but we recently bumped into a problem I don't really know how to deal with. To improve user experience we trigger most of the bean-validation logic using AJAX (using p:ajax update="errorMessageForFieldWhatever"), for example when the user tabs out of a text field, changes a value in a dropdown, etc so that they have immediate feedback about the error. This all works fine when dealing with field/property-level constraints but I don't see how to make it work properly with class-level bean validators. Let me illustrate with an example.
Assume the following entity object, CDI bean and facelets view, and a custom bean validator which requires that MaxValue is greater than MinValue.
#Entity
#CustomClassLevelBeanValidator
public class Model {
#Min(10) int minValue; int maxValue; // getters/setters ommitted }
#Named
#SessionScoped
public class ModelBean {
#Valid private Model model; // getter and initialization ommitted }
<f:form>
min value:
<p:inputText id="minValue" value="#{modelBean.model.minValue}">
<p:ajax process="#this" update="minValueErrorMessage"/>
</p:inputText>
<p:message for="minValue" id="minValueErrorMessage"/>
max value:
<p:inputText id="maxValue" value="#{modelBean.model.maxValue}"/>
<p:message for="maxValue"/>
</f:form>
What we want to achieve is the following:
When the user tabs out of minValue, the error message for that field gets updated. This already works because of standard JSF/single-field-bean-validation integration.
When the user tabs out of EITHER minValue or maxValue, the error message for maxValue should be updated. Note that this really consists of 4 separate cases: the constraint can become valid as well as invalid through changes in minValue and the same goes for maxValue. I'm not clear how to make this work without resolving to JSF-level validation.
Current state of affairs
Direct updating of single-field error messages on ajax events already works (out of the box).
By making use of MyFaces' ExtVal component we also managed to trigger class-level validations on form submit, although all constraint violations end up in the "global errors" section (p:messages globalOnly, which makes sense since during class-level bean validation you do not specify which property failed validation).
We already implemented a solution which is functionally equivalent to what I lined out above (from a user's perspective) but I hate it. It involves a lot of process=this/update=that on the facelets side and sometimes the use of JSF-level validation thereby violating DRY since we'll have to repeat those constraints in the domain model again to make sure webservice calls are properly validated, too.
If it turns out that what we want to achieve is not possible/feasible we'll have to settle for triggering field-level constraints through AJAX and process all the cross-field stuff on form submit. It's not that bad actually but I'm hoping we can do better. Coming from a .NET background I remember this kind of stuff being reasonably easy to implement using WPF and IDataErrorInfo.
Solution requirements
An ideal solution would satisfy all of the following requirements:
Be fully implemented using Bean Validation alone, no FacesMessages etc
Allows direct feedback to the end user after editing a form field, on validation errors on that specific field and all other fields whose constraints are affected by it
Shows validation errors "where they belong", e.g. in the above example the rule "max > min" is, at least from a user's perspective, tied to the "maxValue" field. The fact that such a constraint is not strictly an error on maxValue but rather a relation between both fields doesn't really matter, I should be able to pick one of the two as the "victim" for validation and present the end user with the message "sorry, that specific field is wrong".
I understand that this is not possible in the general case having constraints over N fields some of which may not even be in the current view, but I think stuff like min > max, endDate > startDate etc could be covered.
Where to go from here?
As far as I'm aware there's nothing in JSF, BeanValidation or PrimeFaces that let's me achieve this. I'm not so sure about ExtVal, it seems designed to be very extensible but even then I wouldn't know where to start. If there's anything in any of these libraries which I completely overlooked and that let's me solve this problem, please let me know!
If not, what would it take to build a custom solution to this problem? I've thought about manually implementing this, something along the lines of a custom phaselistener which triggers all bean validators for all submitted fields in the current views and turns them into FacesMessages. However I suspect this will not be an easy task:
Standard class-level ConstraintViolations don't carry a leafBean/property-path, without that, validation error's probably can't be matched to jsf's client-ids
JSF does not apply model values if any of them fails validation. In cross-field validation scenarios it is possible that applying a single value violates a constraint, while applying all of them would make the object valid again (how does ExtVal do this? does it not follow JSF's rules?)
Do we validate during PROCESS_VALIDATIONS? If so, should we enable DISABLE_DEFAULT_BEAN_VALIDATOR context param to allow otherwise invalid model values to populate the entity?
It seems part of the problem is that JSR303 sees constraint validation as a state (an object is either valid or not) while JSF sees it as an action (no, you can't submit this form, it's invalid). Will JSF2.2 make life easier in this regard? I wouldn't mind a user submitting invalid values, we'll just make sure not to store them in the DB ourselves. At least this solves the problem of having to reset UIInput components.
The longer I think about this the more I suspect it's just not going to work the way I want it to. Still I feel kind of stupid having to tell our users that "no sorry, end date must be after start date is such a complicated business rule that we cannot give feedback directly, you'll only bump into that error when you submit the entire form." So if anyone comes up with a solution which fullfills all requirements I'd be very grateful to hear about it.
Ok I know scope questions come up all the time but I'm interested in a slightly different approach to the solution. The #ViewScope is a fantastic bridge between the #RequestScope and the #SessionScope.
However there is still a common use case (at least for me) where I really don't want to use #SessionScope but I need the data over a couple of views. A really simple case is when I have multiple datatables chained together each one depending on previous selections.
It's perfectly possible to use <f:paramView> and pass a single or even a couple of pieces of data as params in the address and then retrieve everything from the database again. I am more interested in finding a way of creating a 'snapshot' of the beans state / variables, creating the new #ViewScope and then 'restoring' the 'snapshot state' to the new bean.
Does such a thing exist? Ideas? Opinions?
I don't know if this is the 'accepted solution' but I've implemented an idea that works for me. (Feedback appreciated!)
So I have created a #SessionScoped class with a couple of static maps:
private static Map<String, Object> objectVariableMap;
// Getters, setters and methods etc. are omitted for simplicity
The idea being that I have specified a map that accepts a String as the key and an Object as the value. I've specifically not set the type of object to allow me to store any type of object in there. The caveat is that you need to be sure of the type of object when retrieving it so you can cast it back into its original type.
Now comes the time to set the data from the first #ViewScoped. I generate a random UUID (or what ever you want) as the Map key and then set the value to the object I'm working with (ie. this, or indeed any other objects you might want to pass to the next view). Save the key, value into the map and set the URL param to the key.
I'm never keen on passing data like user id's etc. in URL params (even when its encrypted). This idea has the added benefit of offering disposable URL values that have a specifiable life span.
On the receiving end (ie. The new #ViewScoped bean, or any other scope for that matter) you read in the URL param (the map key) using <f:paramView> and then use a preRenderView event to retrieve and set the Object where working with.
At this point you can choose to remove the key pair from the Map and invalidate the ability to retrieve that object or you can keep keep the key pair for a longer duration by simply updating the object if there are any changes.
UPDATE: Conceptually this has been really successful (for me at least). I've created a handfull of useful methods and classes surrounding the concept to make it more universal. If anybody wants more specific instructions or I might even create a small library if anybody wants.
You can use the CDI "Conversation Scope" for this. This is narrower than the session scope but wider than the view scope.
If the pages between which you pass parameters are a unit, you can also make them a flow in JSF 2.2 and use the flow scope.
Projects like CODI offer various other scopes that can be used between pages.
I have a strange thing, I'm using dynamic field binding in a custom control.
The field binding is created like this.
XPage (Datasource "document" is placed here)
Custom Control (String passed in)
(to get errors if there are any)
Repeat (CompositeData is passed to a bean that returns the strings for Rows,columns)
Repeat (repeat 1 variable used for Columns)
Custom Control (fieldname is passed in)
field binding is done like this
#{document[compositeData.fieldName]}
The problem is that when I save the XPage I get an error in the messages control
Document has been saved by another user - Save created a new document as a response to that modified document.
And all fields are cleared.
Any ideas how to debug this or is there something I'm missing?
The "Document has been saved by another user" error is only tip of the iceberg - there are some really strange problems with reapeats that repeats fields that are bound and repeatControls property is set to false. The decoding part of xpages lifecycle cannot handle it properly - the controls will be losing data. You should use repeatControls set to true as Martin suggests.
"Repeat control variable doesn't exists" is probably caused by the property that removes repeats set to true. You can solve this by either changing it to false or by adding additional data context that will keep repeated value.
And finally for this to have add/remove functionality You can use Dynamic Content Control and show(null) hack to rebuild the repeat content.
To manage this complexity better I would advise You to stop using document data source and start creating some managed beans.
If You will follow my suggestions I guarantee that You will get the functionality You are looking for as I have few apps that works great and have this kind of complex data editors in them.
I don't know if it'll help you, but I pass both the document datasource and the field name as parameters to a DynamicField control, and use it like this:
compositeData.dataSource[compositeData.fieldName]
The type of the datasource is com.ibm.xsp.model.DataSource, it's listed as dataInterface under Data Sources.
Do you have repeatControls="true" set for the repeat control?
It sounds like you've got the datasource defined multiple times on the XPage (plus custom controls). Either that or the save button has save="true" but the code saves the document back-end, or code in multiple places saves the same document. I've used the same method of passing the datasource down to the custom control, but that may just be because that was what I saw on a blog.