I got two password fields at my JSF - and error messages for them, so if confirmation password is not equal to defining password - err message should appears on the screen. But it also appears even if fields are equals but defining password not passed validation (so it passed to confirmation password validator as null)
so - this is my defining password JSF snippet
<div class="item"
style="height: initial">
<p:outputLabel id="l_passDef" for="passDef"
value="#{msgs['customerForm.defaultPassword']}"/>
<p:password id="passDef" widgetVar="passDefVar" value="#{customerBean.customer.password}"
feedback="true" label="Default Password"
redisplay="true" required="true"
requiredMessage="#{msgs['Error.passDef.mandatory']}"
validatorMessage="#{msgs['Error.passDef.wrongFormat']}"
binding="#{passDef}">
<p:ajax event="keyup" oncomplete="hideCustomerErrMsg('passDef')"/>
<f:validateRegex pattern="(?=.{8,})((?=.*\d)(?=.*[a-z])(?=.*[A-Z])|(?=.*\d)(?=.*[a-zA-Z])(?=.*[\W_])|(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_])).*"/>
</p:password>
<div class="pass-message">
<p:message id="m_passDef" for="passDef" display="text"/>
</div>
</div>
so - this is my confirmation password JSF snippet
<div class="item">
<p:outputLabel id="l_passConf" for="passConf"
value="#{msgs['customerForm.confirmPassword']}"/>
<p:password id="passConf" widgetVar="passConfVar" value="#{customerBean.customer.password}"
label="Confirm Password" redisplay="true" required="true"
requiredMessage="#{msgs['Error.passConf.mandatory']}"
>
<f:attribute value="#{passDef}" name="passDef"/>
<f:validator binding="#{confirmPasswordValidator}"/>
<p:ajax event="keyup" oncomplete="hideCustomerErrMsg('passConf')"/>
</p:password>
<div class="pass-message">
<p:message id="m_passConf" for="passConf" display="text"/>
</div>
</div>
and this is my validator
#Component
#Scope("request")
public class ConfirmPasswordValidator implements Validator {
private static final Logger LOGGER = Logger.getLogger(ConfirmPasswordValidator.class);
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
ResourceBundle resourceBundle = PropertyResourceBundle.getBundle("messages/messages_en");
if (value == null) {
return;
}
UIInput uiInput = (UIInput) component.getAttributes().get("passDef");
String defaultPassword = (String) uiInput.getValue();
String confirmPassword = (String) value;
if ((!confirmPassword.equals(defaultPassword))&&(defaultPassword != null)) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "",
resourceBundle.getString("Error.passConf.confirm")));
}
}
}
Please help me how to hide err message if password fields are equal but first (defining) password field not passed validation
Related
A form with validations on several input fields should show stylish error messages with an icon in front of the message contained in a red box.
The h:panelGroup containing icon and message must be rendered only in case of an error.
With just one validated input field this would work:
<h:panelGroup rendered="#{facesContext.validationFailed}"
But with more than one input field all error panel groups are visible, even those without error showing no text but the icon in a red box.
My solution is now to have a validator for each input and add an attribute ´´validationFailed´´ to each validator.
<p:inputNumber id="mileage" validator="#{mileageValidator.validate}"></p:inputNumber>
<h:panelGroup rendered="#{mileageValidator.validationFailed}" styleClass="wizard-alert-box">
<div class="wizard-alert-content-margin">
<h:graphicImage name="attention.png"/>
<h:message id="invalid-mileage" for="mileage" showSummary="true" showDetail="false"/>
</div>
</h:panelGroup>
<p:calendar id="date" validator="#{dateValidator.validate}"></p:calendar>
<h:panelGroup rendered="#{dateValidator.validationFailed}" styleClass="wizard-alert-box">
<div class="wizard-alert-content-margin">
<h:graphicImage name="attention.png"/>
<h:message id="invalid-date" for="date" showSummary="true" showDetail="false"/>
</div>
</h:panelGroup>
And here is one of the validators:
#Named("mileageValidator")
public class MileageValidator {
#Getter
private boolean validationFailed;
public void validate(FacesContext context, UIComponent component, Object value) {
validationFailed = value == null;
if (validationFailed) {
throw new ValidatorException(new FacesMessage("mileage empty"));
}
}
}
I'm kind of new to JSF and I'm having trouble to understand what values JSF renders in a form after its validation fails. Im using WebSphere 7 and its default implementation of JSF, MyFaces (I think 2.0).
My xhtml looks like this:
<h:form id="form">
<h:inputText id="text" value="#{backing.text}" required="true"/>
<h:message for="text" />
<h:selectManyListbox id="options" value="#{backing.options}" required="true">
<f:selectItem itemLabel="1" itemValue="1" />
<f:selectItem itemLabel="2" itemValue="2" />
<f:selectItem itemLabel="3" itemValue="3" />
</h:selectManyListbox>
<h:message for="options" />
<h:commandButton value="Save" />
</h:form>
And my backing bean like this:
public class Backing {
private String text;
private String[] options;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String[] getOptions() {
return options;
}
public void setOptions(String[] options) {
this.options = options;
}
}
I fill the <h:inputText /> with some text.
I select two options from the <h:selectManyListbox />
I press the 'Save' button
The form is rendered with the value I entered for <h:inputText /> and with the options I selected on the <h:selectManyListbox /> (no validation messages are shown, as expected)
Now ...
I empty the <h:inputText />
I deselect the two options from the <h:selectManyListbox />
I press the 'Save' button
The form renders the <h:inputText /> empty and the <h:selectManyListbox /> with the previous options I had selected (both validation messages are shown, as expected)
As you can see, the behaviour when rendering the <h:inputText /> and the <h:selectManyListbox /> is different:
<h:inputText /> renders component's submitted value
<h:selectManyListbox /> renders bean's value
I've been trying to render <h:selectManyListbox /> with no options selected without hacking or messing my code, but had no luck.
¿Is this some bug? ¿Am I missing something?
The less hacky solution I found is to copy and re-implement the method renderOption, overriding the default MenuRenderer.
The original source is something like this as I had to decompile (version 1.2_13).
Notice that I'm pasting only the lines that actually need to be changed. If you need to use this solution you will have to copy the full contents of the method:
public class MenuRenderer extends HtmlBasicInputRenderer {
protected void renderOption(FacesContext context, UIComponent component, Converter converter, SelectItem curItem, Object currentSelections, Object[] submittedValues, HtmlBasicRenderer.OptionComponentInfo optionInfo) throws IOException {
(...)
Object valuesArray;
Object itemValue;
if (submittedValues != null) {
boolean containsValue = containsaValue(submittedValues);
if (containsValue) {
valuesArray = submittedValues;
itemValue = valueString;
} else {
valuesArray = currentSelections;
itemValue = curItem.getValue();
}
} else {
valuesArray = currentSelections;
itemValue = curItem.getValue();
}
(...)
}
}
I created CustomListboxRenderer (ListboxRenderer extends MenuRenderer) like this:
public class CustomListboxRenderer extends ListboxRenderer {
#Override
protected void renderOption(FacesContext context, UIComponent component, Converter converter, SelectItem curItem, Object currentSelections, Object[] submittedValues, HtmlBasicRenderer.OptionComponentInfo optionInfo) throws IOException {
(...)
Object valuesArray;
Object itemValue;
if (submittedValues != null) {
valuesArray = submittedValues;
itemValue = valueString;
} else {
valuesArray = currentSelections;
itemValue = curItem.getValue();
}
(...)
}
}
and then added in faces-config the next lines:
<render-kit>
<renderer>
<component-family>javax.faces.SelectMany</component-family>
<renderer-type>javax.faces.Listbox</renderer-type>
<renderer-class>CustomListboxRenderer</renderer-class>
</renderer>
</render-kit>
I have a form to enter information including 3 field
textbox user name (requiere=true)
Combobox bank name (requiere=true)
Combobox bank branches (requiere=true)
I want when the user selects the bank, the bank branch will load without filling out form (particular user does not need to fill in the textbox: "user name")
For example : my xhmtl form
<h:form id="ftextform">
<s:validateAll id="ValidateAll">
<fieldset>
<div class="entry">
<h:outputLabel for="name" styleClass="label #{invalid?'errors':''}">name<em>*</em></h:outputLabel>
<h:inputText id="name" value="#{branch.name}" required="true" />
</div>
<div class="entry">
<h:selectOneMenu id="creditBank" value="#{branch.creditBank}" immediate="true">
<f:selectItems value="#{fExtBankList}"></f:selectItems>
<a:support id="onkeyup" event="onchange" actionListener="#{branch.creditBankchange}" reRender="searchResults"/>
</h:selectOneMenu>
</div>
<a:outputPanel id="searchResults">
<div class="entry">
<h:selectOneMenu id="creditBankBranch" value="#{branch.creditBankBranch}">
<f:selectItems value="#{branch.creditBankBranchList}"></f:selectItems>
</h:selectOneMenu>
</div>
</a:outputPanel>
</fieldset>
<fieldset>
<div class="buttonBox">
<h:commandButton id="check" value="Cancel" action="#{branch.cancel}" immediate="true"/>
<h:commandButton id="next" value="Next" action="#{branch.next}"/>
</div>
</fieldset>
</s:validateAll>
</h:form>
my bean :
#Name("branch")
public class Branch implements IBranch
{
private static int count = 0;
private String creditBank;
private String creditBankBranch = "aaa";
private String name;
private List<SelectItem> creditBankBranchList = new ArrayList<SelectItem>();
// action
public void creditBankchange()
{
SelectItem e = new SelectItem(creditBank + count, creditBank);
creditBankBranchList.add(e);
}
....
the simple answer is to use <a4j:region>
details:
view(xhtml)
<h:form id="ftextform">
<fieldset>
<div class="entry">
<h:outputLabel for="name" styleClass="label #{invalid?'errors':''}">name<em>*</em></h:outputLabel>
<h:inputText id="name" value="#{branch.name}" required="true">
<s:validate/>
</h:inputText>
<div class="errors"><h:message for="name"/></div>
</div>
<a:region>
<div class="entry">
<h:selectOneMenu id="creditBank" value="#{branch.creditBank}" immediate="true" >
<f:selectItems value="#{branch.creditBankList}"></f:selectItems>
<a:support status="globalStatus" event="onchange" reRender="searchResult"
action="#{branch.creditBankchange}"/>
</h:selectOneMenu>
<div class="errors"><h:message for="creditBank"/></div>
</div>
<s:div style="width: 300px" id="searchResult" immediate="true">
<h:selectOneMenu id="creditBankBranch" value="#{branch.creditBankBranch}" >
<f:selectItems value="#{branch.creditBankBranchList}"></f:selectItems>
</h:selectOneMenu>
<div class="errors"><h:message for="creditBankBranch"/></div>
</s:div>
</a:region>
</fieldset>
<fieldset>
<div class="buttonBox">
<h:commandButton id="check" value="Cancel" action="#{branch.cancel}" immediate="true"/>
<h:commandButton id="next" value="Next" action="#{branch.next}"/>
</div>
</fieldset>
</h:form>
and bean:
public class Branch implements IBranch
{
private String creditBank;
private String creditBankBranch;
private String name;
private List<SelectItem> creditBankBranchList = new ArrayList<SelectItem>();
private List<SelectItem> creditBankList = extBankList();
// action
public void creditBankchange()
{
extBankBranchList();
}
// (test)create test banks list
private List<SelectItem> extBankList()
{
List<SelectItem> list = new ArrayList<SelectItem>();
for(int i =0; i < 10; i ++)
{
list.add(new SelectItem(i, Integer.toString(i)));
}
return list;
}
// (test)load bank branchs list from bank
private void extBankBranchList()
{
this.creditBankBranchList.clear();
for(int i =0; i < 10; i ++)
{
this.creditBankBranchList.add(new SelectItem(i, "bank " + this.creditBank + "branch " + Integer.toString(i) ));
}
}
I have a small webapplication for a library, with a customized ISBN validator. My .xhtml page for adding a book looks like this:
<fieldset>
<h:messages/>
<ul>
<li>
<h:outputLabel for="isbn" value="#{labels.isbn}:" />
<h:inputText id="isbn" value="#{addController.book.isbn.isbnValue}" required="true" requiredMessage="- ISBN must be filled in.">
<f:validator validatorId="isbnValidator" />
</h:inputText>
</li>
<li>
<h:outputLabel for="title" value="#{labels.title}:" />
<h:inputText id="title" value="#{addController.book.title}" required="true" requiredMessage="- Title must be filled in."/>
</li>
<li>
<h:outputLabel for="name" value="#{labels.name}:" />
<h:inputText id="name" value="#{addController.book.person.name}" required="true" requiredMessage="- Name must be filled in."/>
</li>
<li>
<h:outputLabel for="firstname" value="#{labels.firstname}:" />
<h:inputText id="firstname" value="#{addController.book.person.firstname}" />
</li>
</ul>
<h:commandButton id="addButton" action="#{addController.save}" value="#{labels.add}" />
<h:commandButton id="cancelButton" action="bookOverview" value="#{labels.cancel}" />
</fieldset>
</ui:define>
The isbnValidator of the first input field is this class:
private Isbn isbn;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
isbn = new Isbn((String) value);
if (!isbn.isIsbn17Characters()) {
addMessageToContext(context, "- ISBN needs to have 17 characters");
}
if (isbn.isIsbn17Characters() && !isbn.isIsbnInRightFormat()) {
addMessageToContext(context, "- Wrong format, it should be like 'XXX-XX-XXX-XXXX-X'");
}
if (isbn.isIsbn17Characters() && isbn.isIsbnInRightFormat() && !isbn.isIsbnFormatValid()) {
addMessageToContext(context, "- ISBN can only contain numbers, and no other tokens");
}
if (isbn.isIsbn17Characters() && isbn.isIsbnInRightFormat() && isbn.isIsbnFormatValid()
&& !isbn.isLastNumberValid()) {
addMessageToContext(context, "- Last number of the ISBN should be " + isbn.getCorrectLastNumber()
+ " with those 12 numbers");
}
}
public static void addMessageToContext(FacesContext context, String message) {
FacesMessage facesMessage = new FacesMessage();
facesMessage.setSummary(message);
facesMessage.setDetail(message);
context.addMessage("isbn", facesMessage);
}
When I click the 'add' button, the book should be added to the database.
When the ISBNfield, namefield or titlefield aren't filled in, I get the corresponding error message. But when my fields are filled in, and the ISBN validation fails, he shows the error message, but he still adds the book (with a wrong ISBN-number) to the database.
A solution which I thought about: If my messages tags is not empty, he shouldn't add the book to the database. But how can I check that?
Or is there a better solution to my problem?
The validation error message handling is wrong. You need to throw a ValidatorException with a FacesMessage instead of adding the FacesMessage manually.
So instead of all those
addMessageToContext(context, "- ISBN needs to have 17 characters");
you need to do
throw new ValidatorException(new FacesMessage("- ISBN needs to have 17 characters"));
This is for JSF sign that the input is invalid and hence the action method won't be invoked. You also don't need to specify the client ID, it will just end up in the <h:message> associated with the input component where this validator is fired on.
How would you compare two string for equality in a JSF Validator?
if (!settingsBean.getNewPassword().equals(settingsBean.getConfirmPassword())) {
save = false;
FacesUtils.addErrorMessage(null, "Password and Confirm Password are not same", null);
}
Use a normal Validator and pass the value of first component as attribute of second component.
<h:inputSecret id="password" binding="#{passwordComponent}" value="#{bean.password}" required="true"
requiredMessage="Please enter password" validatorMessage="Please enter at least 8 characters">
<f:validateLength minimum="8" />
</h:inputSecret>
<h:message for="password" />
<h:inputSecret id="confirmPassword" required="#{not empty passwordComponent.value}"
requiredMessage="Please confirm password" validatorMessage="Passwords are not equal">
<f:validator validatorId="equalsValidator" />
<f:attribute name="otherValue" value="#{passwordComponent.value}" />
</h:inputSecret>
<h:message for="confirmPassword" />
(note that binding in above example is as-is; you shouldn't bind it to a bean property!)
with
#FacesValidator(value="equalsValidator")
public class EqualsValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
Object otherValue = component.getAttributes().get("otherValue");
if (value == null || otherValue == null) {
return; // Let required="true" handle.
}
if (!value.equals(otherValue)) {
throw new ValidatorException(new FacesMessage("Values are not equal."));
}
}
}
If you happen to use JSF utility library OmniFaces, then you can use <o:validateEquals> for this. The exact case of "confirm password" is demonstrated on <o:validateEqual> showcase.