0

Just wanted to ask for the best way to solve my problem: I got a form with multiple inputs/selects and also buttons to add new objects to the databean behind the form. Now my problem is the validation. E.g.: in one of those selects i chose an option. If this option is selected the user has to add a specific type of an object to the large databean behind the form. That's done by selecting values in two further selects. As soon as those are selected (they are submitted onchange by ajax) the user generates a new object by hitting an "Add" button.

On submit those added object (consisting of two values) must have a specific enum type depending on the first selected value and so on..

Now this all (and a lot more) should be validated and error/info messages should be added to the view.

Now my question: whats the best way to achieve this? The error message has to be at a specific place in the view.

I thought of multiple hidden input fields and using the validate attribute to call validation methods of the underlying bean (cause bean values are needed too). And then rendering an error message right at the position of the hidden field. Is this a good solution? Working with the validator attribute and validating the whole form would lead to error messages in areas where the user hasn't made any input yet, right? I would like to avoid that and just validate the current area. Is that possible by using one bean? Otherwise i would have to use multiple validators with a lot of attributes, or use the beanManager to get my underlying bean?

Jasper de Vries
  • 19,370
  • 6
  • 64
  • 102
sofarsoghood
  • 243
  • 2
  • 16
  • i added some more details, written bold :) thx for the link! but that would be just one huge validation method with dozens if not hundred lines of code. Is there a possibility to split that up? – sofarsoghood Oct 06 '16 at 08:51
  • @sofarsoghood If you want to split the logic, use my method + the bean validation framework. As per comment in my example code - turn off the validation in the form (using ), or you will get two validations instead of one. – fdreger Oct 06 '16 at 08:59
  • @fdreger: yeah that sounds interesting, the only problem is, that i cannot throw a ValidationException at that place (which would be nice), because that phase is alredy skipped at that time, but i think i will try out that way! thx! – sofarsoghood Oct 06 '16 at 09:03
  • @sofarsoghood: you don't want to throw validation exception, because you want to validate ALL the errors and go on with validating even if one error occurred. – fdreger Oct 06 '16 at 09:07
  • @sofarsoghood btw - throwing an exception from JSF validators seems to be a sort of a dirty hack (and a Java antipattern - using exception for standard application flow) - used just to be able to signal an error and an error message at the same time (an alternative of returning a null for "valid" or a non-null string for "invalid with the message" would be worse). BVF is more modern and does away with this exception. – fdreger Oct 06 '16 at 09:16
  • @fdreger this showcase no. 3 https://www.mkyong.com/jsf2/custom-validator-in-jsf-2-0/ also does it by throwing an exception, dont think that this is a dirty solution, because it interrupts further executions and insta renders the message of the exception :) – sofarsoghood Oct 06 '16 at 10:04
  • @sofarsoghood: these are two, unrelated items. 1) A basic UX issue: when a form has 2 errors in two different fields, your user expects both of them to be displayed. Skipping validation of an email because there is an error in expiry date would be strange. This is why you don't want to interrupt execution and insta-render the message. 2) The (very) old JSF API uses an anti-pattern of using exceptions for flow control as a lesser evil. Newer APIs avoid this. Also, Mkyong's code is usually very bad and only works well in a very narrow scope. It's better not to rely on his judgement. – fdreger Oct 06 '16 at 11:22

1 Answers1

1

The JSF validation is done per component, not per form. This is by design (and it has a good purposes). There is a lot of misunderstanding on this point and many teams make the mistake of shoehorning their business logic validation into component validation, just because the word "validation" sounds right to them.

The validation you need is different - it's a business level validation. You don't really validate state of a single component, but rather coherence of the data, which is behind the components. You can still use the message framework to display the errors where you want them.

The clean and "proper" way to do it in JSF will be to:

  • skip the JSF component validation (because it's designed for different case than yours); you want all the values from the component to be put into your backing beans;

  • put anywhere you need an error message displayed;

  • write validation logic in your action method. You can use Bean Validation Framework or write the logic by hand;

  • if no errors are found, the action method performs an action;

  • on error, depending on the error, display a message in a proper place (using the component id);

  • if any error occured, don't perform the action and return from the action method.

The action method should look somewhat like this (this is not a copy-paste example, it's just to give you an idea of the flow):

  public void doSomething() {
     boolean valid = true;

     if (startData.after(expiryDate)) {
        FacesContext.getCurrentInstance().addMessage("expiryDate", new FacesMessage("Expiry date should come after creation date"));
        valid = false;
     }

     // other checks

     // checks could also make use of BVF validator - but then it's better to switch the BFV validation for the whole form
     // (if you don't, then any single field validation will disable error checking for the business validation, which
     // will look strange for your users)
     if (!valid) {
        // if we returned immediately upon detecting an error, we would only display the first one.
        return;
     }

     // do the real stuff

  }
fdreger
  • 12,264
  • 1
  • 36
  • 42
  • Isn't it better to use bean method validation? on "last" input component in form, set validator="#{myBean.doSomething}", **but** method have to have same signature as other validators i.e. **public void doSomething(FacesContext context, UIComponent component, Object value) throws ValidatorException** - this way validation is executed in **Process Validations** phase, and not in **Invoke Application** phase – Nikola Oct 06 '16 at 09:29
  • @Nikola: No, because: 1) the cross field validation *will not work* in such case 2) this needlessly couples the order of inputs with the validation logic 3) There is *absolutely no reason* to prefer "process validations" over the "invoke application" phase in this scenario. From the semantic point of view, we actually much prefer the "invoke application" phase, which should be used for data-level manipulation, not the "validations" which is only designed for component-level operations. – fdreger Oct 06 '16 at 09:38