How can I skip bean validation if all fields for the entity are empty?
Asked Answered
C

2

4

I have a address entity like this:

@Entity
public class Address implements java.io.Serializable {
    @Id @GeneratedValue(strategy = IDENTITY)
    private Long id;

    @Size(max = 100)
    private String street;

    @Size(max = 15)
    private String nr;

    @Field
    @Size(min = 1, max = 20) @NotBlank
    private String city;
}

This is part of several other entities. For one such entity the address is optional. I have a view where our users can edit the whole entity via several form inputs, including several address fields.

To be able to edit the address, I initialize the parent entity with an empty address entity.

Now the problem is: I don't want to persist an Address on the entity when all address fields are empty. For that to work, I need to skip validation for the entity if (and only if) all fields of Address are empty. How do I do that in the most elegant way?

What I'm currently doing is to skip bean validation altogether with <o:validateBean disabled="true" /> for the submit button, setting the address to null if it comes out empty. But this sounds like a total hack as it disables all validation in all cases. How can I do better?

Curvature answered 5/7, 2013 at 15:18 Comment(0)
J
7

You can just use EL in disabled attribute of <f:validateBean> (and <o:validateBean>). You can use EL to check if any of those fields have been filled. You can use binding attribute to reference a component in EL. You can check if a field has been filled by checking the value of the request parameter associated with component's client ID.

Then, to disable bean validation on those fields when #{addressIsEmpty} evaluates true, it's easier to use <f:validateBean> instead of <o:validateBean> as the former can be used to wrap multiple input components.

So, all with all, this should do:

<c:set var="addressIsEmpty" value="#{empty param[street.clientId] and empty param[nr.clientId] and empty param[city.clientId]}" />

<f:validateBean disabled="#{addressIsEmpty}">
    <h:inputText binding="#{street}" value="#{bean.address.street}" />
    <h:inputText binding="#{nr}" value="#{bean.address.nr}" />
    <h:inputText binding="#{city}" value="#{bean.address.city}" />
</f:validateBean>

The <o:validateBean> is only easier to use if you intend to control it on a per-input or per-command basis.

Jamila answered 6/7, 2013 at 13:50 Comment(2)
Hm, although checking the param value worked just fine I had to use the string value of the id (I control the naming container and the ids of the inputs), using clientId gave me a NullPointerException when trying to evaluate the EL. Could this be because the values are in a conditionally rendered popup?Curvature
I get Parameter key is null when referencing any binding. Though that's only with the <c:set> or <f:validateBean>, outputting it in <h:outputText/> is fine. Sounds like a view build vs render time issue?Curvature
P
0

Cross validation of multiple fields can be a tough issue. But in this situation it is not validation per se that bothers you in the first place but rather possibility to nullify some instance on some condition in your bean before you'd persist the entity.

The most straightforward way would be to mark all fields associated with Address entity as required="false" in your view not to care of fields being filled in (or just omit it as it's the default) and do all the checks of, and modifications to the placeholder in your action method, right before calling your EJB service. Like so:

public String save() {
    //do some checks
    if((entity.getAddress().getStreet() == null) && ...) {
        //assign address to null
        entity.setAddress(null);
    }
    entityService.persist(entity);
    return "success";
}
Pyroxenite answered 5/7, 2013 at 19:21 Comment(2)
The problem with that is that JSF will do Bean Validation by default, and even when the fields are required=false (they are in my code). Failing that validation, even when totally reasonable, means my save never gets called.Curvature
Yes, that's a good point! As usual, BalusC's answer keeps us on the right track once again.Pyroxenite

© 2022 - 2024 — McMap. All rights reserved.