Add global message when field validation fails - validation

Usually the PrimeFaces tag for messages shows the global as well as the field specific messages. What I want to do is to just show the global messages (globalOnly="true") but also show a common message if the validation of any field fails. No matter how many fields fail, just one message like 'Please correct your data'. The field specific errors are already shown next to the input fields so no need to display them twice.
I'm using composite components for each of my inputs (Textbox, Dropdown, Radios, ...). The validation of each field should be done on blur with textboxes and probably on valuechanged for dropdowns and radios.
Now there are two types of validation I want to handle. First is the standard validators brought by JSF itself. required="true" for example, but also validateRegex, validateLength, ...
And then there are values I've to check against another backend. For those I would probably create methods in my bean and call them as listener of my
<p:inputText id="#{cc.attrs.name}"
value="#{cc.attrs.val}"
styleClass="#{cc.attrs.iconClass}"
required="#{cc.attrs.required}"
requiredMessage="#{cc.attrs.requiredMessage}">
<p:ajax event="blur" process="#form" update="outer-wrapper"
listener="#{cc.attrs.someValidationMethod}" />
</p:inputText>
So basically I want to have just one global message if any of the field validations fails. I know could just render an additional box with rendered="#{facesContext.validationFailed}" but I prefer to have a global message. Is there a out-of-the-box setting or does it have to be implemented?

I use a phase listener to do so. This is roughly my implementation (using OmniFaces):
public class ValidationFailedListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
#Override
public void beforePhase(PhaseEvent event) {
if (Faces.isValidationFailed()) {
Messages.addGlobalError("Your validation failed message");
}
}
#Override
public void afterPhase(PhaseEvent event) {
// NOOP
}
}
You should register it in your faces-config.xml:
<lifecycle>
<phase-listener>your.ValidationFailedListener</phase-listener>
</lifecycle>
My actual implementation uses a message bundle to display a localized message.
If you cannot use OmniFaces, here is the relevant code using vanilla JSF:
if (FacesContext.getCurrentInstance().isValidationFailed()) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Your validation failed message",
null);
FacesContext.getCurrentInstance().addMessage(null, message);
}

Related

Multifield validation with variable number of components

I'm building a form in primefaces and need to run validation on a specific field of all items in a <p:dataList>. Specifically I need to make sure there are only a maximum 3 different values in any number of items in the list.
<p:dataList value="#{myBean.myItems}" var="it"
id="myDataList" rowIndexVar="rowIndex">
...
<p:inputNumber value="#{it.fieldToValidate}">
<f:validator validatorId="myValidator" />
</p:inputNumber>
...
</p:dataList>
And the validator:
#FacesValidator(value = "myValidator")
public class MyValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
if (component == null)
return;
Set<BigDecimal> myValues = new HashSet<BigDecimal>();
//now add all values to the set, but how to get them?
if (myValues.size() > 3) {
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "To many different values",
"There can be only three"));
}
}
}
I have read Validation across multiple fields in JSF/PrimeFaces and
JSF doesn't support cross-field validation, is there a workaround? and
How validate two password fields by ajax?
but they don't solve my problem because I don't know the contents of my data list beforehand.
I am using JSF-2.1.7, Primefaces-6.0.4 and jdk-1.6, ancient I know but it can't be helped...

Is it possible to automatically re-render h:messages on all AJAX calls with JSF2?

I am using an h:messages tag on every page of my JSF2 project to show global messages. I'm also using mostly AJAX to update the page content. As it is right now, I have to include the ID of the h:messages tag in the "render" attribute of every AJAX tag so that it displays any messages that come as a result of that AJAX call. I am wondering, is there some way I can save myself from doing this to every AJAX tag and just tell it to always automatically refresh the messages on any AJAX call?
The easiest way is to go with Primefaces and its <p:messages /> component. Using autoUpdate="true" gives you the chance to have the message component updated everytime.
Alternatively, if not going to use Primefaces and want to avoid having to declare your h:messages id each time, you could use backing side updating. Just implement a JSF PhaseListener, which listens to INVOKE_APPLICATION phase and renders your component after it happens:
public class JsfPhaseListener implements PhaseListener {
#Override
public void afterPhase(PhaseEvent event) {
FacesContext.getCurrentInstance().getPartialViewContext()
.getRenderIds().add("header:messages");
}
#Override
public void beforePhase(PhaseEvent event) {
}
#Override
public PhaseId getPhaseId() {
return PhaseId.INVOKE_APPLICATION;
}
}
This will cause your header:messages component to be rendered on every single request you make.
See also:
Primefaces autoupdate in their blog
p:messages at the showcase
Can I update a JSF component from a JSF backing bean method?
How to implement a PhaseListener which runs at end of lifecycle?

How to validate multiple fields in JSF?

I'm going through validation in JSF and I see lots of examples of very basic logic. Frankly, I put them in the same category where the HelloWorld examples go. I can't imagine placing error messages in xhtml files, using a separate validation method for each validated field or employing bean validation.
What I want to do, is have a single method on the backing bean that will execute validation for each field, logging error messages driven by keys in i18n property files.
Can this be done? If so, how do we register that method as validating method, how do we obtain submitted field values for evaluation, and how do we register error messages?
<h:inputText id="username" value="#{bean.username}" label="UserName" binding="#{bean.component}"/>
<h:message for="username" />
<h:commandButton value="Submit" action="#{bean.actionMethod}" />
In your bean class,
private UIComponent component;
public UIComponent getComponent() {
return component;
}
public void setComponent(UIComponent component) {
this.component = component;
}
public String actionMethod() {
if (!validate()) {
return null;
}
// do your action method logic
}
private boolean validate() {
FacesContext context = FacesContext.getCurrentInstance();
//do validation for your fields and add to faces messages
FacesMessage msg = new FacesMessage(severity, summary, detail);
context.addMessage(component.getClientId(), msg);
// do for other fields
return status;
}
Refer this to get component client id
How to add a message to a specific component from JSF backing bean

Custom JSF validator message for a single input field

I'd like to have different validation messages for every validator for different input fields.
Is it possible in JSF to have a different validation messages for a single validator (e.g. <f:validateLongRange>) for every input field?
There are several ways:
The easiest, just set the validatorMessage attribute of the UIInput component.
<h:inputText ... validatorMessage="Please enter a number between 0 and 42">
<f:validateLongRange minimum="0" maximum="42" />
</h:inputText>
However, this is also used when you use other validators. It will override all messages of other validators attached to the input field, including required="true" and Bean Validation such as #NotNull. Not sure if that would form a problem then. If so, then head to following ways.
Create a custom validator which extends the validator of interest, such as LongRangeValidator in your specific case, wherein you catch the ValidatorException of the super.validate() call and then rethrow it with the desired custom message. E.g.
<h:inputText ...>
<f:validator validatorId="yourLongRangeValidator" />
<f:attribute name="longRangeValidatorMessage" value="Please enter a number between 0 and 42" />
</h:inputText>
with
#FacesValidator("yourLongRangeValidator")
public class YourLongRangeValidator extends LongRangeValidator {
public void validate(FacesContext context, UIComponent component, Object convertedValue) throws ValidatorException {
setMinimum(0); // If necessary, obtain as custom f:attribute as well.
setMaximum(42); // If necessary, obtain as custom f:attribute as well.
try {
super.validate(context, component, convertedValue);
} catch (ValidatorException e) {
String message = (String) component.getAttributes().get("longRangeValidatorMessage");
throw new ValidatorException(new FacesMessage(message));
}
}
}
Use OmniFaces <o:validator> which allows setting a different validator message on a per-validator basis:
<h:inputText ...>
<o:validator validatorId="jakarta.faces.Required" message="Please fill out this field" />
<o:validator validatorId="jakarta.faces.LongRange" minimum="0" maximum="42" message="Please enter a number between 0 and 42" />
</h:inputText>
See also:
Change the default message "Validation Error: Value is required" to just "Value is required"
How to customize JSF conversion message 'must be a number consisting of one or more digits'?
Internationalization in JSF, when to use message-bundle and resource-bundle?
JSF converter resource bundle messages

custom validator call on button through ajax

hi i wrote a custom a validator which gets the system name and compare it against the id in database, now i wanna apply a check if this value is exactly the same, user must be allowed to click the button and move on else some error message should be displayed. and i am really confused how to call the validator() on through ajax.
my view page code is
<h:commandButton action="sample?faces-redirect=true" value="submit">
<f:ajax execute="#{csample.UserValidator}" render="#form" >
<h:inputText name="idtext" value="#{csampleBean.id}" />
</f:ajax>
</h:commandButton>
and my custom validator
public void UserValidator(FacesContext context, UIComponent toValidate, Object value)
throws UnknownHostException, ValidatorException, SQLException, NamingException
{
java.net.InetAddress localMachine = java.net.InetAddress.getLocalHost();
String machine= localMachine.getHostName();
String query = "select * from USER_ where USER_ID = '"+machine+"'";
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
Connection conn = ds.getConnection();
Statement stat = conn.createStatement();
//get customer data from database
ResultSet result = stat.executeQuery(query);
if (query==machine)
// what to do here
conn.close();
need some guidance
You need to create a class implementing the Validator interface. On validation fail, just throw a ValidatorException with a FacesMessage. JSF will then take care that the FacesMessage ends up in the right <h:message> associated with the input component.
You can register the custom validator to JSF by annotating it with #FacesValidator with therein the validator ID. You can reference it in <h:inputXxx validator> or <f:validator validatorId>.
Here's a kickoff example:
#FacesValidator("userValidator")
public class UserValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
if (!valid) {
String message = "Sorry, validation has failed because [...]. Please try again.";
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null));
}
}
}
Which is been used as follows (note: <h:inputText> does not have name attribute! instead use id; also note that your initial code snippet has some nesting which isn't making any sense):
<h:inputText id="idtext" value="#{csampleBean.id}" validator="userValidator">
<f:ajax render="idtextMessage" />
</h:inputText>
<h:message id="idtextMessage" for="idtext" />
<h:commandButton action="sample?faces-redirect=true" value="submit" />
See also:
How to perform validation in JSF, how to create a custom validator in JSF
Unrelated to the concrete problem, your JDBC code is leaking DB resources. Please fix that as well.

Resources