I have a JSF 2.0 application which also uses Primefaces 3.3. Currently there is a nice feature where a label is decorated with an asterisk if the related <p:inputText>
uses a required="true"
attribute.
This field is bound to a bean property which is annotated with @NotNull
validation constraint. It seems redundant and error prone to have to also add required="true"
in the XHTML when the bean property is already annotated with @NotNull.
Is there a hook or some way to automatically decorate labels for components that are bound to properties with @NotNull?
Any thoughts or suggestions are greatly appreciated.
Note: This is a hack. It may have performance implications due to it's use of introspection
At a basic level, what you need to know if the field is annotated with @NotNull
. Perform this check in a sensible place like @PostConstruct
for a view scoped bean. Declare a global variable to determine the required attribute
boolean requiredAttribute;
@PostConstruct
public void init{
Field theField = this.getClass().getField("theField");
NotNull theAnnotation = theField.getAnnotation(NotNull.class);
if(theAnnotation != null){
requiredAttribute = true;
}
}
Bind the required
attribute to the variable in the backing bean
<p:inputText id="inputit" required="#{myBean.requiredAttribute}"/>
This solution is based on PF 6.0, I don't remember if BeanValidationMetadataExtractor
is available in previous versions. Anyway, creating a DIY extractor is a simple task.
I had a similar problem. In my specific case:
UIInput
) is requiredrequired="true"
on the comp since it's already bound to a @NotNull
/@NotBlank
property/fieldSo, here is what I have done:
import java.util.Set;
import javax.el.ValueExpression;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PreRenderComponentEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.validation.constraints.NotNull;
import javax.validation.metadata.ConstraintDescriptor;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import org.omnifaces.util.Faces;
import org.primefaces.context.RequestContext;
import org.primefaces.metadata.BeanValidationMetadataExtractor;
public class InputValidatorConstraintListener implements SystemEventListener
{
@Override
public boolean isListenerForSource(Object source)
{
return source instanceof UIInput;
}
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException
{
if(event instanceof PreRenderComponentEvent)
{
UIInput component = (UIInput) event.getSource();
component.getPassThroughAttributes().computeIfAbsent("data-required", k ->
{
ValueExpression requiredExpression = component.getValueExpression("required");
if(requiredExpression != null || !component.isRequired())
{
FacesContext context = Faces.getContext();
ValueExpression valueExpression = component.getValueExpression("value");
RequestContext requestContext = RequestContext.getCurrentInstance();
try
{
Set<ConstraintDescriptor<?>> constraints = BeanValidationMetadataExtractor.extractAllConstraintDescriptors(context, requestContext, valueExpression);
if(constraints != null && !constraints.isEmpty())
{
return constraints.stream()
.map(ConstraintDescriptor::getAnnotation)
.anyMatch(x -> x instanceof NotNull || x instanceof NotBlank || x instanceof NotEmpty);
}
}
catch(Exception e)
{
return false;
}
}
return false;
});
}
}
}
and declare it in faces-config.xml:
<?xml version="1.0" encoding="utf-8"?>
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<application>
<system-event-listener>
<system-event-listener-class>it.shape.core.jsf.listener.InputValidatorConstraintListener</system-event-listener-class>
<system-event-class>javax.faces.event.PreRenderComponentEvent</system-event-class>
</system-event-listener>
</application>
</faces-config>
With this listener UIInput
s are rendered with a data-required
passthrough attribute:
<input
id="form:editPanelMain:j_idt253"
name="form:editPanelMain:j_idt253"
type="text"
value="Rack Assemply"
size="80"
data-required="true" <============================ NOTE THIS!!
data-widget="widget_form_editPanelMain_j_idt253"
class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all"
role="textbox"
aria-disabled="false"
aria-readonly="false">
Now, I use a css rule to highlight these fields:
input[data-required='true'],
.ui-inputfield[data-required='true'],
*[data-required='true'] .ui-inputfield {
box-shadow: inset 0px 2px 2px #bf8f8f;
}
You can adapt this listener to either set the component as required or use another approach that suits your specific needs.
Another approach could be:
UILabel
s instead of UIInput
sUIInput
associated with the for
/forValue
ValueExpression of the labelUIInput
for validation constraintUIInput.setRequired(true)
The performance impact is negligible, as I have tested complex pages with ~3000 components.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With