Triggering class-level bean validation through AJAX in JSF2/PrimeFaces - jsf

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.

Related

JSF 2.x simplify complex views into a single tag

I have a general JSF problem, I found no nice solution for yet. See the picture for a general idea. I have a workaround solution (sorry for the typo in the image) in place that solves the problem by a listbox. However the desired solution is to display all existing versions next to each other (probably always around 1-3).
I have a view with a tree and picklist. There is a complex flow regarding the interaction between list and tree, e.g. you can only move models to subgroups, not top-level-groups and much more. I created a handler class that manages this behavior and translates it to service calls.
Now, a new requirement came up. There are several versions of this tree that should be displayed all together on one page. My gut feeling is that managing n versions in one handler is a big mess as I need to store several things in the handler already for one version.
In React, I would create a component that wraps the tree and all of the interaction. However, in JSF I'm not so sure what is the best practice here?
I would be happy about suggestions and ideas, I'm not expecting Code :)
I found a solution that fits my needs and I post it here hoping that it might help other people as well :)
So on my view I have several tree views with complex interactions. For example, if an item within the tree is moved, the operation is immediately reflected in the database. As I use JPA, I need to translate this to an entitymanager call.
The views are either displayed in a list or just one-at-a-time via a dropdown select.
Anyway, the idea is that every complex view component has its own controller with a reference to an entitymanager and a transaction, while having just one JSF handler class. If JSF would allow to create multiple handlers (like #{handler_1}, {handler_2}), the problem could be solved in a different way. But as JSF works name based and the name {#handler} always refers to the same container managed thing, this is no option.
The handler class is ViewScoped (or SessionScoped, if you prefer). For each tree component it has a ComponentController class that receives the EntityManager and the UserTransaction as well as the related data form the handler via constructor injection. This way, the handler can delegate all commands to the Controller while being DRY.
With this solution, the controller logic can be re-used regardless how many tree components exist. Each view elements binds a specific controller via handler.controllers.get(id).
All other solutions did not work for me as they are not able to perform database operations on view interactions.

How to maintain the state after validation? (validateWholeBean)

I'm using the <f:validateWholeBean> tag (JSF 2.3) for class-level validation.
The validation is occurring correctly, however, the form data is deleted after the return of the messages with the validation errors.
From what I realized this is the default behavior, but I wanted to know if it is possible to do different, I want the data to remain in the form after the restore view phase. I want the user to see the information that was entered wrong.
I was able to solve my problem by using the <o: validateBean value =" # {bean.product} "/> omnifaces tag in conjunction with the method attribute to" validateActual ". The reported behavior as disadvantage in documentation is ideal for me.
If the copying strategy is not possible due to technical limitations, then you could set method attribute to "validateActual".
<o:validateBean value="#{bean.product}" validationGroups="com.example.MyGroup" method="validateActual" />
This will update the model values and run the validation after update model values phase instead of the validations phase. The disadvantage is that the invalid values remain in the model and that the action method is anyway invoked. You would need an additional check for FacesContext.isValidationFailed() in the action method to see if it has failed or not.
http://showcase.omnifaces.org/validators/validateBean

User roles and workflow status xpages and managed bean

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.

How to match cross-field bean validation errors to a single h:message?

We're currently building a web application based on JSF and PrimeFaces and want to use Bean Validation to validate the domain model. The problem we currently face is that we want most error messages displayed next to a single input field. For example, suppose there's a constraint "enddate > startdate", implemented as a class-level bean validator, we still want constraint violations for that rule to show up in a h:message/p:message specific to the enddate field rather than in the globalOnly section. Can this be achieved? If so, how?

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 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.

Resources