How to validate two composite component fields in JSF? - validation

I have two input date fields startDate & endDate based on same composite component 'comps:dateTimeZone'.
inputpage.xhtml
<div>
<comps:dateTimeZone id="startDate" date="#{myBean.startDate}">
</comps:dateTimeZone>
<comps:dateTimeZone id="endDate" date="#{myBean.endDate}">
</comps:dateTimeZone>
</div>
Need to validate
if one is present, then other is also present.
the interval/difference is within 15 days.
dateTimeZone.xhtml
<cc:interface>
<cc:attribute name="date" type="java.util.Date" required="true"/>
<cc:attribute name="timeZoneName" type="java.lang.String" required="false"/>
</cc:interface>
<cc:implementation>
<h:inputText id="date-input"
value="#{cc.attrs.date}"
required="true"
requiredMessage="Date is required."
immediate="true">
<f:converter converterId="dateTimeZoneConverter" />
<f:attribute name="dateTimeZone" value="#{cc.attrs.timeZoneName}"/>
</h:inputText>
</cc:implementation>
Since both are based on composite component, the usual approach with
inputpage.xhtml having
<comps:dateTimeZone id="endDate" date="#{myBean.endDate}">
<f:validator validatorId="daterangevalidator" for="dateinputfieldref"/>
</comps:dateTimeZone>
dateTimeZone.xhtml having
<cc:editableValueHolder name="dateinputfieldref" targets="date-input" />
DateRangeValidator.java having
#FacesValidator("dateRangeValidator")
public class DateRangeValidator implements Validator {
#Override
public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) throws ValidatorException {
final Date endDateValue = (Date) value;
//Below doesn't work as uiComponent is tied to only endDate.
final Date startDateValue = (Date) uiComponent.getAttributes().get("startDate");
}
}
doesn't seem to help. It is bound to only one field at a time. So unable to get access to the other field 'startDate'.
Pls suggest how to perform this validation based on 2 date fields. How to get handle of both the fields at the same time ? like
if(startDate!=null & endDate==null){
return valid;
}
if (endDate-StartDate > 15days){
return invalid;
}

Related

Implement callback method in JSF composite component

I'd like to implement a "callback" feature in my composite component. This method would be called from the backing component when necessary.
The XHTML part is:
<cc:interface componentType="partnerSelComp">
<cc:attribute name="value" type="com.app.data.Partner"/>
<cc:attribute name="callback" method-signature="void notify(java.lang.Long)"/>
</cc:interface>
<cc:implementation>
<span id="#{cc.clientId}">
<p:inputText id="code" binding="#{cc.partnerCode}">
<p:ajax event="blur" update="code name msg" listener="#{cc.validate}" />
</p:inputText>
<p:outputLabel id ="name" binding="#{cc.partnerName}"/>
<p:message id="msg" for="code"/>
</span>
</cc:implementation>
The relevant part of backing component is:
#FacesComponent("partnerSelComp")
public class PartnerSelComp extends UIInput implements NamingContainer {
private InputText partnerCode;
private OutputLabel partnerName;
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
public void validate() {
...
MethodExpression exp = (MethodExpression) getAttributes().get("callback");
...
}
...
}
Above the exp gets null value. How can I get the callback attribute, and how can I execute it?
A solution (Java 1.8) to call a function with no args.
Component:
<composite:interface>
<composite:attribute name="buttonCallback" type="java.util.function.Consumer"/>
</composite:interface>
<composite:implementation>
<p:commandButton value="Submit" action="#{cc.attrs.buttonCallback.accept(null)}"/>
<composite:implementation>
Usage in view:
<mycomp:myCC buttonCallback="#{myBackingBean.myCallback}"/>
Usage in backing bean:
private Consumer<Void> myCallback;
#PostConstruct
public void init() {
myCallback= this::myFunction;
}
public void myFunction(Void v) { //... }

jsf inputText don't execute change listener event

i have two .xhtml pages with two similar inputText elements ("customer id" and "customer name"), in both pages the inputText for "customer id" launch a request ajax event to looking for the name of the customer and to put it in the inputText for "customer name".
when i run the web application and use the first pages everything work fine, but if i don't use the first page and go to the second page (via commandlink in first page) without use the first page then the inputText for search the customer name don't work, indeed not enter to the testBean2.searchCustId method.
the code:
first page
<h:form>
<h:commandLink action="page2">PAGE 2</h:commandLink><br/>
<h:outputText value="customer id: "/>
<h:inputText id="custoId" label="CUSTOMER_ID" value="#{testBean.custId}" required="true">
<a4j:ajax event="change" listener="#{testBean.searchCustId(testBean.custId)}" render="custoName"/>
<f:validateLength minimum="1" maximum="12"/>
</h:inputText>
<rich:message for="custoID" ajaxRendered="true"/>
<h:outputText value="Customer name: "/>
<h:inputText id="custoName" label="CUSTOMER_NAME" value="#{testBean.custName}"/>
<rich:message for="custoName" ajaxRendered="true"/>
</h:form>
second page
<h:form>
<h:outputText value="customer id: "/>
<h:inputText id="custoId2" label="CUSTOMER_ID2" value="#{testBean2.custId}" required="true">
<a4j:ajax event="change" listener="#{testBean2.searchCustId(testBean2.custId, 0)}" render="custoName2"/>
<f:validateLength minimum="1" maximum="12"/>
</h:inputText>
<rich:message for="custoId2" ajaxRendered="true"/>
<h:outputText value="customer name: "/>
<h:inputText id="custoName2" label="CUSTOMER_NAME2" value="#{testBean2.custName}"/>
<rich:message for="custoName2" ajaxRendered="true"/>
</h:form>
now jsf managed bean
testBean
public class TestBean {
private String custId;
private String custName;
public TestBean() {
}
//getter and setter
public void searchCustId(String ced){
custName = ced + "- SOME CUSTOMER NAME"; //THIS IS FOR TESTING
}
}
testBean2
public class TestBean2 {
private String custId;
private String custName;
public TestBean2() {
}
//getter and setter
public void searchCustId(String ced, int que){
custName = ced + " - " + que; //THIS IS FOR TESTING
}
}
what may be happening?
i'm new on this and my english is not good :)
You can use f:setPropertyActionListener.
<a4j:ajax event="change" listener="#{myBean.listener}">
<f:setPropertyActionListener target="#{myBean.someProperty}" value="your_value_here"/>
</a4j:ajax>
But from your code it looks like you are trying to pass the very one value that's used on h:inputText, that's not necessary:
<h:inputText id="custoId" label="CUSTOMER_ID" value="#{testBean.custId}" required="true">
<a4j:ajax event="change" listener="#{testBean.searchCustId}" render="custoName"/>
<f:validateLength minimum="1" maximum="12"/>
</h:inputText>
After Ajax event the values testBean.searchCustId will automatically be updated on Managed bean after passing the validation, so your listener could be:
public class TestBean {
private String custId;
private String custName;
public TestBean() {
}
//getter and setter
public void searchCustId(){
custName = custId + "- SOME CUSTOMER NAME"; //THIS IS FOR TESTING
}
}

Change default message of f:validateRegex

I have input for email:
<h:inputText value="#{registrationBean.email}" id="email" label="#{msgs.email}">
<f:validateLength minimum="5" maximum="50" />
<f:validateRegex
pattern="^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$" />
</h:inputText>
<h:message for="email"/>
So, how I can change message, when validateRegex not matched? Now message is:
Regex pattern of '^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$' not matched
But I want something like „Incorrect e-mail…”
Thanks.
You can use the validatorMessage attribute of inputText. In your case you can use this:
<h:inputText value="#{registrationBean.email}" id="email" label="#{msgs.email}" validatorMessage="Incorrect e-mail…">
<f:validateLength minimum="5" maximum="50" />
<f:validateRegex pattern="^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$" />
</h:inputText>
<h:message for="email"/>
You can use validator:
public class EmailValidator implements Validator {
private static Pattern patt = Pattern.compile(Constants.getEmailRegExp());
#Override
public void validate(FacesContext arg0, UIComponent comp, Object arg2)
throws ValidatorException {
if (arg2 == null || arg2.toString().trim().length() == 0) {
return;
}
if (!patt.matcher(arg2.toString().trim()).matches()) {
String label = (String) comp.getAttributes().get("label");
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, (label == null || label.trim().length() == 0 ? "Email: " : label+": ") +
"Invalid format", null));
}
}
}
Register it via annotation or in faces-config.xml:
<validator>
<validator-id>email-validator</validator-id>
<validator-class>path.EmailValidator</validator-class>
</validator>
And use it
<h:inputText id="email" label="#{msg.email}"
value="#{registrationForm.email}"
size="40" maxlength="80" required="true">
<f:converter converterId="lowerCase" />
<f:validator validatorId="email-validator" />
</h:inputText>

Validating three dependent h:selectOneMenus

i want to validate a jsf selectOne Menu:
<h:selectOneMenu id="day" value="#{registerService.dayOfBirth}">
<f:selectItems value="#{registerService.days}" />
<f:validator validatorId="dateValidator" />
<f:attribute name="monthId" value="#{component.parent.parent.clientId}:month" />
<f:attribute name="yearId" value="#{component.parent.parent.clientId}:year" />
</h:selectOneMenu>
<h:selectOneMenu id="month" value="#{registerService.monthOfBirth}">
<f:selectItems value="#{registerService.months}" />
</h:selectOneMenu>
<h:selectOneMenu id="year" value="#{registerService.yearOfBirth}">
<f:selectItems value="#{registerService.years}" />
</h:selectOneMenu>
My Validator is this:
#FacesValidator(value="dateValidator")
public class DateValidator implements Validator {
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String year = (String) component.getAttributes().get("yearId");
String month = (String) component.getAttributes().get("monthId");
UIInput yearInput = (UIInput) context.getViewRoot().findComponent(year);
UIInput monthInput = (UIInput) context.getViewRoot().findComponent(month);
FacesMessage message = null;
int yearValue = (Integer) yearInput.getValue();
int monthValue = (Integer) monthInput.getValue();
int dayValue = (Integer) value;
Calendar calendar = Calendar.getInstance();
calendar.set(yearValue, monthValue, dayValue);
int daysOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
if (daysOfMonth < dayValue) {
message = new FacesMessage ("Date is not valid!", "Email Validation Error");
message.setDetail("This month does not have so many days!");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
And i get this error message:
java.lang.IllegalArgumentException: j_idt12
at javax.faces.component.UIComponentBase.findComponent(UIComponentBase.java:655)
at de.hof.tschuwwa.controller.validators.DateValidator.validate(DateValidator.java:27)
at javax.faces.component.UIInput.validateValue(UIInput.java:1165)
at javax.faces.component.UISelectOne.validateValue(UISelectOne.java:146)
at javax.faces.component.UIInput.validate(UIInput.java:983)
at javax.faces.component.UIInput.executeValidate(UIInput.java:1249)
at javax.faces.component.UIInput.processValidators(UIInput.java:712)
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1261)
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1261)
at javax.faces.component.UIForm.processValidators(UIForm.java:253)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:552)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1689)
at javax.faces.component.UIForm.visitTree(UIForm.java:371)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:399)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:263)
... 32 more
So the exception is thrown here:
UIInput yearInput = (UIInput) context.getViewRoot().findComponent(year);
My other validators are all working but they arent with selectOneMenu they
are with inputText.
You can't pass autogenerated IDs. Give the parent naming container component a fixed ID:
<h:form id="myForm">
Or, better, just pass the whole component along:
<f:attribute name="month" value="#{month}" />
<f:attribute name="year" value="#{year}" />
...
<h:selectOneMenu binding="#{month}" ... />
<h:selectOneMenu binding="#{year}" ... />
and obtain them as follows:
UIInput yearInput = (UIInput) component.getAttributes().get("year");
UIInput monthInput = (UIInput) component.getAttributes().get("month");
Or, better yet, create a composite component based on the whole.

JSF validate on submit form

I'm working on a JSF 2.0 form, I have a managedbean with 2 fields
import java.util.Date;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class StackOverflow {
private Date firstDate;
private Date secondDate;
public void submit(){
//validate here and show error on form
}
}
and the xhtml like:
<h:inputText value="#{stackOverflow.firstDate}">
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:inputText value="#{stackOverflow.secondDate}">
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:commandLink action="#{stackOverflow.submit}">
<span>Submit</span>
</h:commandLink>
I want to validate the first and second date that the second date is not before the first date
Here's one of the ways:
<h:messages globalOnly="true"/>
<h:form>
<h:inputText value="#{stackOverflow.firstDate}" binding="#{firstDate}">
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:inputText value="#{stackOverflow.secondDate}" validator="dateValidator">
<f:attribute name="firstDate" value="#{firstDate}" />
<f:convertDateTime pattern="d/M/yyyy" />
</h:inputText>
<h:commandButton value="Submit" action="#{stackOverflow.submit}"/>
</h:form>
with
#FacesValidator(value="dateValidator")
public class DateValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
UIInput sd = (UIInput)component.getAttributes().get("firstDate");
Date firstDate = (Date)sd.getValue();
Date secondDate = (Date)value;
if(!firstDate.before(secondDate)){
FacesMessage msg = new FacesMessage("Entered dates are invalid: first date must be before second date");
throw new ValidatorException(msg);
}
}
}

Resources