I have three input texts in my xhtml and I want to check if the sum of those three fields equals to 100. What is the best way of validating multiple fields in my situation? Is it possible to use a validator?
...
<h:inputHidden id="inputhiddenId" value="true" validator="#{managedBean.checkSum}"/>
<h:message for="inputhiddenId" style="color: red"/>
...
and
#ManagedBean
#ViewScoped
public class ManagedBean implements Serializable {
...
public void checkSum(FacesContext context, UIComponent component, Object value) {
double val = filed1 + field2 + field3;
if (val != 100.0) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
"Sum of fields must be equal to 100");
throw new ValidatorException(msg);
}
}
}
solved my problem.
But also <h:inputText ... components for field1, field2 and field3 must embrace <f:ajax .../>.
Related
I get javax.faces.FacesException: java.lang.NullPointerException when I type something in the zip code and hit submit with country set to default null value. If I select the country and then type something everything works. I tried SubmittedValue, but it is working the opposite way - with null is working and after this is giving null exception.
#FacesValidator("zipV")
public class ZipValidator implements Validator {
LocaleBean Bean = new LocaleBean();
String language;
private static final String ZIP_PATTERN_BG = "\\d{4}";
private static final String ZIP_PATTERN_US = "\\d{5}";
private static final String ZIP_PATTERN_DEFAULT = "[A-Za-z0-9]*";
private String zip_pattern;
private Pattern pattern;
private Matcher matcher;
private String country;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
language = Bean.getLanguage();
UIInput Input = (UIInput) component.getAttributes().get("country");
country = Input.getValue().toString();
String zip = (String) value;
if (country == null || country.isEmpty()) {
return;
}
switch (country) {
case "BGR":
zip_pattern = ZIP_PATTERN_BG;
break;
case "USA":
zip_pattern = ZIP_PATTERN_US;
break;
default:
zip_pattern = ZIP_PATTERN_DEFAULT;
break;
}
pattern = Pattern.compile(zip_pattern);
matcher = pattern.matcher(value.toString());
if (!matcher.matches()) {
switch (language) {
case "en": {
FacesMessage msg = new FacesMessage("Invalid zip.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
case "bg": {
FacesMessage msg = new FacesMessage("Невалиден пощенски код.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
}
}
Here's the view:
<h:selectOneMenu id="country" value="#{account.country}" required="true" requiredMessage="#{msg['register.required']}" binding="#{country}">
<f:selectItem itemValue="#{null}" itemLabel="#{msg['register.countryQ']}"/>
<f:selectItems value="#{account.countries}"/>
<f:ajax listener="#{account.loadStates()}" render="state"/>
</h:selectOneMenu>
<h:inputText id="zipcode" required="true" requiredMessage="#{msg['register.required']}" value="#{account.zipcode}">
<f:validator validatorId="zipV"/>
<f:attribute name="country" value="#{country}"/>
</h:inputText>
Here,
country = Input.getValue().toString();
you should not be using toString() at all. You should be casting it:
country = (String) Input.getValue();
Otherwise it will throw NullPointerException if the getValue() returned null. As its javadoc clearly says, a NullPointerException will be thrown among others when you attempt to call an instance method on null (like as you did with toString()).
Please note that this problem is technically unrelated to JSF. It's just basic Java. The java.lang package of the exception is a very good hint in this. If you got an exception of javax.faces (or javax.el) package, then we can talk about a true JSF (or EL) problem.
See also:
jsf validate two fields in one time
JSF doesn't support cross-field validation, is there a workaround?
Unrelated to the concrete problem, I'd really respect Java naming conventions. Variable names start with lowercase. Use input instead of Input. Also, your manual control of localization is strange. What if you ever need to support 10 languages? Do you expand the switches over all place? Make use of JSF builtin localization facilities with <resource-bundle> and ResourceBundle#getBundle().
I have a simple request scoped entity / pojo which has a Enum and a String as properties.
public Enum Type
{
None,
Email,
Fax;
}
#ManagedBean(name = "testEntity")
#RequestScoped
public class TestEntity
{
private Type type; //Default = None
private String address;
//getter and setter
}
This Enum has a field 'Email' which identifies a e-mail address with a related address.
In JSF I now want to enable/disable a validator of a address InputText field regarding the currently selected type in a SelectOneMenu.
<h:form id="formId">
<p:selectOneMenu id="type" value="#{testEntity.type}>
<p:ajax event="change" update=":formId:address"/>
<f:selectItem itemLabel="E-mail" itemValue="Email"/>
<f:selectItem itemLabel="Fax" itemValue="Fax"/>
</p:selectOneMenu>
<p:inputText id="address" value="#{testEntity.address}">
<f:validator validatorId="emailValidator" disabled="#{testEntity.type != 'Email'}"/>
</p:inputText>
<!-- button to call bean method with testEntity as param -->
</h:form>
It is not working the validator is never active but the ajax call is working since I can see the change value in other fields.
That's unfortunately not possible. The <f:xxx> tags are taghandlers (not UI components) which run during view build time, not during view render time. So if it's disabled during building of the view, it'll always be disabled until the view is recreated (e.g. by new request or a non-null navigation).
You'd need to have a "global" validator which delegates further to the desired validator based on the type attribute.
E.g.
<p:inputText ... validator="#{testEntity.validateAddress}" />
with
public void validateAddress(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (type == Email) {
context.getApplication().createValidator("emailValidator").validate(context, component, value);
}
}
Update OmniFaces has recently added a new <o:validator> tag which should solve exactly this problem as follows:
<o:validator validatorId="emailValidator" disabled="#{testEntity.type != 'Email'}"/>
See the showcase example here.
Maybe someone is interested in how I solved it thanks to BalusC help.
Pass type component clientId to custom converter.
<f:attribute name="typeComponentId" value=":formId:type"/>
Validator:
public class TestEntity implements Validator
{
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
{
final String typeComponentId = (String)component.getAttributes().get("typeComponentId");
final UIInput compType = (UIInput)context.getViewRoot().findComponent(typeComponentId);
if(compType != null)
{
final Type type = (Type)compType.getValue();
if(type == Type.Email)
new EmailValidator().validate(context, component, value);
}
}
}
Edit:
Not working inside a ui:repeat component such as p:datatable.
I have an table of data presented by an ui:repeat. Because I want the user to be able to change the data on a per row basis, each row is enclosed in an h:form. Finally each h:form has a button with the f:ajax tag. I am getting wildly inconsistent behavior.
<ui:repeat value="#{importManager.items}" var="item" varStatus="status">
<h:form>
<tr>
<td>
<h:outputText value="#{status.index}"/>
</td>
<td>
<h:inputText id="title" value="#{item.title}" styleClass="#{item.validTitle ? 'valid' : 'invalid'}"/>
</td>
<td>
<h:inputText id="artist" value="#{item.artist}" styleClass="#{item.validArtist ? 'valid' : 'invalid'}"/>
</td>
<td>
<h:commandButton value="#{importMessages.submit}">
<f:ajax execute="#form" render="#all" listener="#{importManager.publish(item)}"/>
</h:commandButton>
</td>
</tr>
</h:form>
</ui:repeat>
The above works but obviously is not cheap on bandwidth.
If I change the render="#all" to render="#form", Firebug shows the partial response being sent ok, but my browser (Firefox) mysteriously does not display it. So I am guessing it (the browser) does not find the element to update?
If I change execute="#form" to execute="#all" I get very strange behavior, namely the data gets lost, and the affected fields go blank.
The backing bean is quite simple:
public void publish(final Item item)
{
Set<ConstraintViolation<Item>> violations = item.validate();
if (violations.isEmpty())
{
temporaryRegistry.deleteItem(item);
registry.storeItem(item);
}
else
{
// Display error messages
}
}
And the model:
#Entity
public class Item implements Cloneable
{
#Id #GeneratedValue
private long identifier;
#NotNull(groups={Warning.class})
#Length(min=1, max=80, groups={Warning.class})
private String title;
#NotNull(groups={Warning.class})
#Length(min=1, max=80, groups={Warning.class})
private String artist;
#NotNull(groups={Warning.class})
#Length(min=1, max=10, groups={Warning.class})
private String media;
#NotNull(groups={Warning.class})
#Length(min=1, max=5, groups={Warning.class})
#Column(name = "_condition")
private String condition;
// Setters and Getters
public boolean isValidTitle()
{
final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
final Validator validator = factory.getValidator();
final Set<ConstraintViolation<Item>> violations = validator.validateProperty(this, "title", Warning.class);
return violations.isEmpty();
}
public boolean isValidCondition()
{
final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
final Validator validator = factory.getValidator();
final Set<ConstraintViolation<Item>> violations = validator.validateProperty(this, "condition", Warning.class);
return violations.isEmpty();
}
public boolean isValidArtist()
{
final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
final Validator validator = factory.getValidator();
final Set<ConstraintViolation<Item>> violations = validator.validateProperty(this, "artist", Warning.class);
return violations.isEmpty();
}
#Override
public boolean equals(final Object object)
{
return (object instanceof Item) && (object != null) && (((Item) object).getIdentifier() == identifier);
}
#Override
public int hashCode()
{
return Long.valueOf(identifier).hashCode();
}
public Set<ConstraintViolation<Item>> validate()
{
final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
final Validator validator = factory.getValidator();
return validator.validate(this, Warning.class);
}
}
Can anyone explain this, and does anyone have a way to submit the form and the form only by ajax and display the result?
Your HTML ends up having a <form> around each <tr>. This is illegal HTML syntax and the browser behaviour is therefore unspecified.
You need to put the <h:form> around the <table>. If you need a form around a single "row", then you might want to redesign the single <table> to be multiple <table>s with fixed column widths or a group of <div>s.
This question already has answers here:
JSF doesn't support cross-field validation, is there a workaround?
(2 answers)
Closed 5 years ago.
can I validate two interdependent fields in with one validator?
<h:form>
<h:inputText value="#{logRegBean.person.name}" >
<f:validator validatorId="loginCorrectValidator" />
</h:inputText>
<h:inputSecret value="#{logRegBean.person.password}" />
<h:commandButton action="#{logRegBean.login}" />
</h:form>
I want to search for the user in the DB and if there is the user, I'll test if the passwords(in db and inputted) match. But how can I access even the password field in one validator? I tried to evaluate the value int the other field via createValueExpression(), but it looks like I can't access the value in that time since I always get empty strings.
Best what you can do is to grab the other UIInput component by UIViewRoot#findComponent() inside the validate() method and then determine the submitted value by either UIInput#getSubmittedValue() (when it occurs after the currently validated component in the component tree) or UIInput#getValue() (when it occurs before the current component and thus is already validated).
E.g.
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
UIInput otherInput = (UIInput) context.getViewRoot().findComponent("clientId");
String otherValue = (String) otherInput.getSubmittedValue();
// ...
}
See also:
JSF doesn't support cross-field validation, is there a workaround?
The validation mechanism in JSF was designed to validate a single component.
However, in practice, you often need to ensure that related components have reasonable values before letting the values propagate into the model.
For example, it is not a good idea to ask users to enter a date into a single textfield.
Instead, you would use three different textfields, for the day, month, and year.
If the user enters an illegal date, such as February 30, you would like to show a validation error and prevent the illegal data from entering the model.
The trick is to attach the validator to the last of the components. By the time its validator is called, the preceding components passed validation and had their local values set. The last component has passed conversion, and the converted value is passed as the Object parameter of the validation method.
Of course, you need to have access to the other components. You can easily achieve that access by using a backing bean that contains all components of the current form. Simply attach the validation method to the backing bean:
public class BackingBean {
private int day;
private int month;
private int year;
private UIInput dayInput;
private UIInput monthInput;
private UIInput yearInput;
// PROPERTY: day
public int getDay() { return day; }
public void setDay(int newValue) { day = newValue; }
// PROPERTY: month
public int getMonth() { return month; }
public void setMonth(int newValue) { month = newValue; }
// PROPERTY: year
public int getYear() { return year; }
public void setYear(int newValue) { year = newValue; }
// PROPERTY: dayInput
public UIInput getDayInput() { return dayInput; }
public void setDayInput(UIInput newValue) { dayInput = newValue; }
// PROPERTY: monthInput
public UIInput getMonthInput() { return monthInput; }
public void setMonthInput(UIInput newValue) { monthInput = newValue; }
// PROPERTY: yearInput
public UIInput getYearInput() { return yearInput; }
public void setYearInput(UIInput newValue) { yearInput = newValue; }
public void validateDate(FacesContext context, UIComponent component, Object value) {
int d = ((Integer) dayInput.getLocalValue()).intValue();
int m = ((Integer) monthInput.getLocalValue()).intValue();
int y = ((Integer) value).intValue();
if (!isValidDate(d, m, y)) {
throw new ValidatorException(new FacesMessage("Invalid Date"));
}
}
private static boolean isValidDate(int d, int m, int y) {
//DO YOUR VALIDATION HERE
}
}
Here is your JSP
<html>
<%# taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%# taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<head></head>
<body>
<h:form>
<h:panelGrid columns="3">
<h:inputText value="#{bb.day}" binding="#{bb.dayInput}" size="2" required="true"/>
<h:inputText value="#{bb.month}" binding="#{bb.monthInput}" size="2" required="true"/>
<h:inputText value="#{bb.year}" binding="#{bb.yearInput}" size="4" required="true" validator="#{bb.validateDate}"/>
<h:message for="year" styleClass="errorMessage"/>
</h:panelGrid>
<h:commandButton value="Submit" action="submit"/>
</h:form>
</body>
</f:view>
</html>
Reference:
Core JavaServer™ Faces
By DAVID GEARY, CAY HORSTMANN
Publisher : Addison Wesley
Pub Date : June 15, 2004
ISBN : 0-13-146305-5
I think SeamFaces' s:validateForm feature may be just what you need. (Seam Faces is a very useful library that brings some nifty CDI-based features to JSF.)
I have this form:
<h:form>
<h:outputText value="Tag:" />
<h:inputText value="#{entryRecorder.tag}">
<f:ajax render="category" />
</h:inputText>
<h:outputText value="Category:" />
<h:inputText value="#{entryRecorder.category}" id="category" />
</h:form>
What I'm trying to achieve: When you type in the "tag" field, the entryRecorder.tag field is updated with what was typed. By some logic upon this action the bean also updates its category field. This change should be reflected in the form.
Questions:
What scope shall I use for EntryRecorder? Request may not be satisfactory for multiple AJAX requests, while session will not work with multiple browser windows per one session.
How can I register my updateCategory() action in EntryRecorder so that it is triggered when the bean is updated?
Answering point 2:
<h:inputText styleClass="id_tag" value="#{entryRecorder.tag}"
valueChangeListener="#{entryRecorder.tagUpdated}">
<f:ajax render="category" event="blur" />
</h:inputText>
Bean:
#ManagedBean
#ViewScoped
public class EntryRecorder {
private String tag;
private String category;
#EJB
private ExpenseService expenseService;
public void tagUpdated(ValueChangeEvent e) {
String value = (String) e.getNewValue();
setCategory(expenseService.getCategory(value));
}
}
Number 1, anybody?
To point 1, I'll use Request since there is no need to use View and Session is, as you well pointed, completely unnecessary.
For point 2, since you are using <f:ajax/> I suggest making full use of it. Here is my proposal:
xhtml:
<h:form>
<h:outputText value="Tag:" />
<h:inputText value="#{entryRecorder.tag}">
<f:ajax render="category" event="valueChange"/>
</h:inputText>
<h:outputText value="Category:" />
<h:inputText value="#{entryRecorder.category}" id="category" />
</h:form>
Note the use of valueChange event instead of blur (not that blur doesn't work but I find valueChange more 'proper' for a value holder component).
bean:
#ManagedBean
#RequestScoped
public class EntryRecorder {
private String tag;
private String category;
public String getCategory() {
return category;
}
public String getTag() {
return tag;
}
public void setCategory(String category) {
this.category = category;
}
public void setTag(String tag) {
this.tag = tag;
tagUpdated();
}
private void tagUpdated() {
category = tag;
}
}
Unless you really want the tagUpdated method executed only when tag is updated through the view, my proposal looks more clear. You don't have to deal with the events (nor casting) and the tagUpdated method can be private hiding it's functionality from possible misuses.