Omnifaces validateBean: attach message to specific component - jsf

I am using Omnifaces <o:validateBean /> to use JSR303 bean validation with a class level constraint.
The whole thing works quite good. The validation message is displayed in a Primefaces <p:messages /> component. Now I wanted to know whether it is possible to narrow the error message to a more precise component (i. e. a <p:message /> tag on a specific element)? In the example below I want to direct the error message to the <p:message /> component with id="id_msg_anniversary" (or if that is possible, to both id="id_msg_anniversary" and id="id_msg_wedding").
One of my intention is to validate everything with bean validation and not to use <o:validateOrder /> or <o:validateMultiple />.
#DayIsBeforeAnniversary
#Entity
public class Person implements Serializable
{
#Temporal(TemporalType.DATE)
private Date weddingDay;
#Temporal(TemporalType.DATE)
private Date silverWeddingAnniversary;
...
}
<p:messages id="id_messages" />
<h:outputLabel value="Wedding Day" />
<p:message id="id_msg_wedding" display="icon" for="id_wedding" />
<p:calendar
id="id_wedding"
value="#{personController.person.weddingDay}"> <br />
<h:outputLabel value="Silver Wedding Anniversary" />
<p:message id="id_msg_anniversary" display="icon" for="id_anniversary" />
<p:calendar
id="id_anniversary"
value="#{personController.person.silverWeddingAnniversary}" /> <br/>
<p:commandButton
value="test"
action="#{personController.navigate()}"
update="#all"/>
<o:validateBean value="#{personController.person}" />
public class DayIsBeforeAnniversaryValidator implements ConstraintValidator<DayIsBeforeAnniversary, Person>
{
#Override
public boolean isValid(Person person, ConstraintValidatorContext context)
{
return person == null
|| person.getWeddingDay() == null
|| person.getSilverWeddingAnniversary() == null
|| person.getWeddingDay().before(person.getSilverWeddingAnniversary());
}
...
}
public #interface DayIsBeforeAnniversary { ... }
For clarification I could attach more code.

The <o:validateBean> associates its message with the <h:form>. So, all you need to do is to make sure you have a <p:message for="formId"> at the desired place. Do note that you can have multiple of them. Below is a kickoff example.
<h:form id="formId">
<p:inputText id="foo" ... />
<p:message for="foo" />
<p:message for="formId" />
...
<p:inputText id="bar" ... />
<p:message for="bar" />
<p:message for="formId" />
...
<o:validateBean ... />
</h:form>
I however do agree this is not the most elegant solution, surely not when compared with members of ValidateMultipleFields family which has an useful showMessageFor attribute for the purpose. It's only unfortunately technically pretty convoluted to implement it on <o:validateBean> too as there's technically no direct relationship between BV constraint violation errors and JSF input fields, so it has to be manually inspected based on bean properties and JSF component tree, which is not exactly an oneliner work.

Related

JSF / PrimeFaces - How to display message for custom validator but not for required validation on the same component

I have a JSF2 implementation using PrimeFaces. I am using a <p:selectOneRadio /> component. When the user selects "No" in the radio component, I would like to show a custom message. I have created a custom validator for this purpose, and along with the message component everything works fine. However, the component is also required. I do not want the "Value is required" validation error for the required component to show up in the message component. In this case, I only want the field and the label to be highlighted.
So, the question is: How do I specify that a given <p:message /> component display only the error from my custom validator, and not the error from the required validator?
I saw an answer to a similar question from a few years ago that said you could set the message for attribute to a component that doesn't actually exist, and add the message to that component via the FacesContext. Such as this:
<p:message for="fooMsg" /> ... FacesContext.getCurrentInstance().addMessage("fooMsg",msg)
However, that does not seem to work. JSF throws an error that it cannot find the component "fooMsg".
Here is my current code.
Component:
<p:outputLabel for="foo" id="foolbl"
value="Some label text." />
<br />
<p:message for="foo" />
<p:selectOneRadio id="foo" layout="pageDirection"
widgetVar="fooVar" required="true">
<f:selectItem itemLabel="Yes" itemValue="Yes" />
<f:selectItem itemLabel="No" itemValue="No" />
<p:ajax update="#this foolbl" process="#this" />
<f:validator validatorId="FooValidator" />
</p:selectOneRadio>
Validator:
#FacesValidator("FooValidator")
public class FooValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value != null) {
String sValue = (String) value;
if (sValue.trim().equalsIgnoreCase("No")) {
FacesMessage msg = new FacesMessage("Summary",
"Detail");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
}
First of all this is default JSF behavior. To solve your
problem, you could use jQuery to check if no radio button is choosen and hide the message in that
case, for example:
<h:form id="form">
<p:outputLabel for="foo" id="foolbl" value="Some label text." />
<br />
<p:message id="foomsg" for="foo" />
<p:selectOneRadio id="foo" layout="pageDirection" widgetVar="fooVar"
required="true">
<f:selectItem itemLabel="Yes" itemValue="Yes" />
<f:selectItem itemLabel="No" itemValue="No" />
<p:ajax process="#this" update="#this foolbl foomsg" />
<f:validator validatorId="FooValidator" />
</p:selectOneRadio>
<p:commandButton process="#this foo" update="foo foolbl foomsg"
oncomplete="if( !PF('fooVar').checkedRadio.length ) $('#form\\:foomsg').hide();" />
</h:form>
Have a look at the oncomplete attribute of the commandButton.

JSF - How can I retain the transient values within a ui:repeat after an immediate action

I created a very simple example based on my project in order to illustrate my doubt. Just a way to register a person with a list of telephone numbers.
MainController.java
private String name;
private List<Phone> phoneList;
// Getters and Setters
#PostConstruct
private void init() {
phoneList = new ArrayList<>();
}
public static class Phone implements Serializable {
private String number;
// Getters and Setters
#Override
public String toString() {
return number != null ? number : "null";
}
}
public void add() {
phoneList.add(new Phone());
}
public void save() {
System.out.println("Name: " + name + "; " + phoneList.toString());
}
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
In my example, note that all phone fields that are added MUST be filled in (required = true).
The problem is: when I type name and click add (to add a phone) the value of the field is maintained. But when I type a first phone and click add, the phone's value is not maintained. This occurs for all fields within the component ui:repeat.
Is there a way to preserve the input values within a after an immediate request, as with the name field?
Extra note: Other strange behavior I noticed is when add at least two phone fields, let the first blank and fills the second, and saves the form. After a failed validation (due to phone blank), click add will make all fields are filled with the value of the second phone.
Wildfly 9.0.2, JSF Api (Jboss) 2.2.12
Thanks to #BalusC comment. The OmniFaces library has two taghandlers that can be used in this case. In both cases input values will be preserved in case of validation failure. Note that h:commandButton should be with <h:commandButton immediate="false" />.
ignoreValidationFailed
In this case all validation failures will be ignored (including converter failures). Note that the h:form have to be changed to o:form. Also, the failures messages will still be displayed, which can be solved putting a proper condition in the rendered attribute. The files will look like this:
index.xhtml
<o:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:ignoreValidationFailed />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</o:form>
<h:messages rendered="#{facesContext.validationFailed}" />
skipValidators
In this case only the validation failures will be ignored (the converters will still run). The failures messages will not be displayed, except for the converters. Note that this taghandler is only available since the 2.3 version. The files will look like this:
index.xhtml
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText value="#{phone.number}" required="true" />
</ui:repeat>
<h:commandButton action="#{mainController.add()}" value="Add Phone">
<o:skipValidators />
</h:commandButton>
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
The solution that I use to this problem is to create an external field to the loop, which stores a JSON containing the values that should be saved. This field, to be outside the loop, properly saves values after each try and restore the missing values when necessary. I use two functions JavaScript and JQuery library.
So the files would look like this:
index.xhtml
<h:outputScript library="jquery" name="jquery.min.js" />
<h:outputScript library="all" name="all.js" />
<h:form>
<h:inputText value="#{mainController.name}" required="true" />
<ui:repeat var="phone" value="#{mainController.phoneList}" varStatus="status">
<h:inputText styleClass="savePhoneNumber" value="#{phone.number}" required="true" onchange="saveUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')" />
</ui:repeat>
<h:inputHidden id="allPhoneNumber" binding="#{allPhoneNumber}" />
<h:outputScript>loadUiRepeatInput('#{allPhoneNumber.clientId}', 'savePhoneNumber')</h:outputScript>
<h:commandButton action="#{mainController.add()}" value="Add Phone" immediate="true" />
<h:commandButton action="#{mainController.save()}" value="Save" />
</h:form>
all.js
function saveUiRepeatInput(inputAll, inputClass) {
document.getElementById(inputAll).value = JSON.stringify($('.' + inputClass).map(function() { return this.value; }).get());
}
function loadUiRepeatInput(inputAll, inputClass) {
var jsonAll = document.getElementById(inputAll).value;
if (jsonAll) {
var array = JSON.parse(jsonAll);
$('.' + inputClass).each(function(i) { if (i < array.length) this.value = array[i]; });
}
}
Although work perfectly (including via ajax, with some minor changes), it looks like a hack, not an ideal solution. So if anyone can help with any solution strictly based on JSF, I will be grateful. Thanks.

How validate two password fields by ajax?

I'm trying to validate two password fields with JSF but no good until now, I search for it on google but everything was about JSF 1.2 and pretty confusing, I'm using JSF 2.0.
This is what I'm doing so far:
<h:outputLabel for="password" value="Password:" />
<h:inputSecret id="password" value="#{register.user.password}" >
<f:ajax event="blur" listener="#{register.validatePassword}" render="m_password" />
</h:inputSecret>
<rich:message id="m_password" for="password"/>
<h:outputLabel for="password_2" value="Password (again):" />
<h:inputSecret id="password_2" value="#{register.user.password_2}" >
<f:ajax event="blur" listener="#{register.validatePassword}" />
</h:inputSecret>
This is how I it is my controller:
public void validatePassword() {
FacesMessage message;
if (!user.getPassword().equals(user.getPassword_2()) ){
message = new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "different password");
}else{
message = new FacesMessage(FacesMessage.SEVERITY_INFO, null, "ok");
}
FacesContext.getCurrentInstance().addMessage("form:password", message);
}
Any idea guys ?
First of all, use a real Validator to validate the input. Don't do it in an action event method.
As to your concrete problem, you just need to specify the both fields in the execute attribute of the <f:ajax>, it namely defaults to the current component only. If you attach a validator to the first input and send the the value of the second input along as a <f:attribute>, then you will be able to grab it in the validator. You can use the binding attribute to bind the component to the view. This way you can pass its submitted value along by UIInput#getSubmittedValue().
Here's a kickoff example:
<h:outputLabel for="password" value="Password:" />
<h:inputSecret id="password" value="#{bean.password}" required="true">
<f:validator validatorId="confirmPasswordValidator" />
<f:attribute name="confirm" value="#{confirmPassword.submittedValue}" />
<f:ajax event="blur" execute="password confirm" render="m_password" />
</h:inputSecret>
<h:message id="m_password" for="password" />
<h:outputLabel for="confirm" value="Password (again):" />
<h:inputSecret id="confirm" binding="#{confirmPassword}" required="true">
<f:ajax event="blur" execute="password confirm" render="m_password m_confirm" />
</h:inputSecret>
<h:message id="m_confirm" for="confirm" />
(note that I added required="true" to both components and also note that you don't necessarily need to bind the confirm password component value to a managed bean property, it's worthless over there anyway)
with this validator
#FacesValidator("confirmPasswordValidator")
public class ConfirmPasswordValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String password = (String) value;
String confirm = (String) component.getAttributes().get("confirm");
if (password == null || confirm == null) {
return; // Just ignore and let required="true" do its job.
}
if (!password.equals(confirm)) {
throw new ValidatorException(new FacesMessage("Passwords are not equal."));
}
}
}
With seam 2 you have the component <s:validateEquality> and you don't need to write code. For JSF2 then you have Seam 3 modules, particulary Faces module and Cross-field Form Validation. An example :
First you have to use the s:validateForm tag:
<h:form id="passwordForm">
<h:inputSecret id="newPassword"
required="true"
redisplay="true"
value="#{passwordController.newPassword}">
</h:inputSecret>
<h:inputSecret id="confirmationPassword"
value="#{passwordController.confirmPassword}"
required="true"
redisplay="true">
</h:inputSecret>
<h:commandButton id="submit" value="Submit" action="#{passwordController.submitPassword}" />
<s:validateForm validatorId="passwordValidator" />
</h:form>
and the corresponding Validator for the password form above would look like this:
#FacesValidator("PasswordValidator")
public class PasswordValidator implements Validator
{
#Inject
#InputField
private String newPassword;
#Inject
#InputField
private String confirmPassword;
#Override
public void validate(final FacesContext context, final UIComponent comp, final Object values) throws ValidatorException
{
if (!confirmPassword.equals(newPassword))
{
throw new ValidatorException(new FacesMessage("Passwords do not match!"));
}
}
}
You can use Primefaces p:password tag. Please see demo example. It has match attribute which should be the id of confirm password.
<p:panel header="Match Mode">
<p:messages id="messages" showDetail="true" autoUpdate="true"/>
<h:panelGrid columns="2" id="matchGrid">
<h:outputLabel for="pass" value="Password " />
<p:password id="pass" value="#{passwordBean.password}" match="confirmPass" required="true"/>
<h:outputLabel for="confirmPass" value="Confirm Password " />
<p:password id="confirmPass" value="#{passwordBean.confirmPassword}" required="true"/>
</h:panelGrid>
<p:commandButton id="saveButton" update="matchGrid" value="Save" />
</p:panel>

Glassfish complaining about JSF component IDs

I am very new to JSF (v2.0) and I am attempting to learn it at places like netbeans.org and coreservlets.com. I am working on a very simple "add/subtract/multiply/divide" Java webapp and I have run into a problem. When I first started out, the application was enter two numbers and hit a '+' key and they would be automatically added together. Now that I have added more complexity I am having trouble getting the operation to the managed bean. This is what I had when it was just "add":
<h:inputText styleClass="display" id="number01" size="4" maxlength="3" value="#{Calculator.number01}" />
<h:inputText styleClass="display" id="number02" size="4" maxlength="3" value="#{Calculator.number02}" />
<h:commandButton id="add" action="answer" value="+" />
For the "answer" page, I display the answer like this:
<h:outputText value="#{Calculator.answer}" />
I had the proper getters and setters in the Calculator.java managed bean and the operation worked perfectly.
Now I have added the other three operations and I am having trouble visualizing how to get the operation parameter to the bean so that I can switch around it. I tried this:
<h:commandButton id="operation" action="answer" value="+" />
<h:commandButton id="operation" action="answer" value="-" />
<h:commandButton id="operation" action="answer" value="*" />
<h:commandButton id="operation" action="answer" value="/" />
However, Glassfish complained that I have already used "operation" once and I am trying to use it four times here.
Any adivce/tips on how to get multiple operations to the managed bean so that it can preform the desired operation?
Thank you for taking the time to read.
The component id should indeed be unique. This is implicitly required by the HTML specification. You know, all JSF does is just generating the appropriate HTML/CSS/JS code. Give them all a different id or just leave it away, it has no additional value in this specific situation (unless you'd like to hook some CSS/JS on it).
To achieve your functional requirement, you may find f:setPropertyActionListener useful.
<h:commandButton action="answer" value="+">
<f:setPropertyActionListener target="#{calculator.operation}" value="+" />
</h:commandButton>
<h:commandButton action="answer" value="-">
<f:setPropertyActionListener target="#{calculator.operation}" value="-" />
</h:commandButton>
<h:commandButton action="answer" value="*">
<f:setPropertyActionListener target="#{calculator.operation}" value="*" />
</h:commandButton>
<h:commandButton action="answer" value="/">
<f:setPropertyActionListener target="#{calculator.operation}" value="/" />
</h:commandButton>
And have a property operation in your calculator managed bean:
private String operation; // +setter.
You can access it in the getAnswer() method and handle accordingly.
Alternatively, let the buttons each point to a different bean action but which returns all "answer":
<h:commandButton action="#{calculator.add}" value="+" />
<h:commandButton action="#{calculator.substract}" value="-" />
<h:commandButton action="#{calculator.multiply}" value="*" />
<h:commandButton action="#{calculator.divide}" value="/" />
with the following methods in your calculator managed bean:
public String add() {
answer = number1 + number2;
return "answer";
}
public String substract() {
answer = number1 - number2;
return "answer";
}
// etc...
and just let getAnswer() return answer and do nothing else there. That's a more clean separation of responsibilities.

JSF f:selectItem in h:selectManyCheckbox not working in backing bean, but is displayed properly in h:dataTable

The problem occurs with this code:
<h:form>
<rich:panel>
<f:facet name="header">
<h:selectManyCheckbox title="Select which types of requests you want to see"
onchange="submit();" value="#{filterListener.chosenFilters}"
id="selectBoxContainer" >
<f:selectItem id="approvedByITS" itemLabel="Approved by ITS" itemValue="approvedByITS" />
<f:selectItem id="approvedByPO" itemLabel="Approved by Process Owner" itemValue="approvedByPO" />
<f:selectItem id="dob" itemLabel="Date" itemValue="dob" />
<f:selectItem id="externalAssignedTo" itemLabel="External assigned" itemValue="externalAssignedTo" />
<f:selectItem id="internalAssignedTo" itemLabel="Internal assigned" itemValue="internalAssignedTo" />
<f:selectItem id="ITSapprovedBy" itemLabel="ITS approved by" itemValue="ITSapprovedBy" />
<f:selectItem id="severity" itemLabel="Severity" itemValue="severity" />
<f:selectItem id="status" itemLabel="status" itemValue="status" />
<f:valueChangeListener type="no.ngt.tech.rt2.listeners.requestFilterListener" />
</h:selectManyCheckbox>
</f:facet>
<h:dataTable value="#{filterListener.chosenFilters}" var="selects" >
<h:column>
<h:outputText value="#{selects}" />
</h:column>
</h:dataTable>
<br />
<h:messages />
</rich:panel>
</h:form>
As we can see I have the value="#{filterListener.chosenFilters}". The dataTable's value is also the same, so whenever I click one of the selectItem's the dataTable has an element added or removed from it (this is working). In my backing bean I have the following code:
public class requestFilterListener implements ValueChangeListener {
private List<String> chosenFilters;
public requestFilterListener() {
}
public void processValueChange(ValueChangeEvent event) {
System.out.println("processValueChange called");
if (chosenFilters != null) {
System.out.println(chosenFilters.size());
}
}
public List<String> getChosenFilters() {
return chosenFilters;
}
public void setChosenFilters(List<String> chosenFilters) {
this.chosenFilters = chosenFilters;
}
Everytime I click one of the checkboxes, a column is added/removed with the proper data, in my console I get the message "processValueChange called" as I output in the processValueChange method, but during this time chosenFilters is always null, and the if expression is never run. How come? This is a session bean, and I really dont understand why my list cant be used within the backing bean, but is used without a problem by my dataTable.
Thanks for your time in looking into this.
The problem is probably on this tag:
<f:valueChangeListener type="no.ngt.tech.rt2.listeners.requestFilterListener" />
You are instructing the f:valueChangeListener tag to create a new instance of requestFilterListener instead of binding to the one specified by the managed bean configuration. Use the binding attribute to bind to #{filterListener}.

Resources