I want to compare two dates (StartDate and EndDate) and check whether one is before the other. The simplest solution is to just do it on the backing bean and "short-circuit" the method.
However this validation does not happen concurrently with the other form validations. For example if I have another field that requires validation (besides the dates) and has invalid input, I will only get a message for that specific field. Only if the other fields are valid will I get the date validation based on the backing bean.
Anyone have a solution?
However this validation does not happen concurrently with the other form validations.
A backing bean action method is indeed not intented to perform input validation.
Anyone have a solution?
Use the right tool for the job; use a normal Validator.
#FacesValidator("dataRangeValidator")
public class DateRangeValidator implements Validator {
// ...
}
Validating multiple input values with a single validator is in turn however indeed a story apart. Basically, you should be grabbing/passing the other component or its value along into the validate() method implementation. In its simplest form, you could use <f:attribute> for this. Assuming that you're using <p:calendar> to pick dates, here's a concrete kickoff example:
<p:calendar id="startDate" binding="#{startDateComponent}" value="#{bean.startDate}" pattern="MM/dd/yyyy" required="true" />
<p:calendar id="endDate" value="#{bean.endDate}" pattern="MM/dd/yyyy" required="true">
<f:validator validatorId="dateRangeValidator" />
<f:attribute name="startDateComponent" value="#{startDateComponent}" />
</p:calendar>
(note the binding attribute, it makes the component available in the EL scope on exactly the given variable name; also note that this example is as-is and that you should absolutely not bind it to a bean property!)
Where the dateRangeValidator look like this:
#FacesValidator("dateRangeValidator")
public class DateRangeValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (value == null) {
return; // Let required="true" handle.
}
UIInput startDateComponent = (UIInput) component.getAttributes().get("startDateComponent");
if (!startDateComponent.isValid()) {
return; // Already invalidated. Don't care about it then.
}
Date startDate = (Date) startDateComponent.getValue();
if (startDate == null) {
return; // Let required="true" handle.
}
Date endDate = (Date) value;
if (startDate.after(endDate)) {
startDateComponent.setValid(false);
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Start date may not be after end date.", null));
}
}
}
If you happen to use JSF utility library OmniFaces, then you could also just use its <o:validateOrder> component. The requirement can be achieved as follows without the need for a custom validator:
<p:calendar id="startDate" value="#{bean.startDate}" pattern="MM/dd/yyyy" required="true" />
<p:calendar id="endDate" value="#{bean.endDate}" pattern="MM/dd/yyyy" required="true" />
<o:validateOrder components="startDate endDate" />
See also:
JSF doesn't support cross-field validation, is there a workaround?
if you're using PrimeFaces which can limit a minimum and maximum dates. the user could not choose a greater range this is an example:
<p:calendar id="startDate" value="#{bean.startDate}" maxdate="#{bean.endDate}">
<p:ajax event="dateSelect" update="endDate"/>
</p:calendar>
<p:calendar id="endDate" value="#{bean.endDate}" mindate="#{bean.startDate}" disabled="#{empty bean.startDate}">
<p:ajax event="dateSelect" update="startDate"/>
</p:calendar>
As BalusC solution works only if you have one date range to validate on the form, here is an improvement to allow multiple date range validations:
add another <f:attribute> to the endDate calendar component where you specify the binding attribute name to startDate component:
<f:attribute name="bindingAttributeName" value="startDateComponent" />
then in the validator:
String startDateBindingAttrName = (String) component.getAttributes().get("bindingAttributeName");
UIInput startDateComponent = (UIInput) component.getAttributes().get(startDateBindingAttrName);
Taking the Answer BalusC like Base, and for me use in the future...
This can to set the Interval (Year, Month, Week), the Reference Comparation Initial, or Final.
#FacesValidator("dateRangeValidator")
public class DateRangeValidator implements Validator {
#Override
public void validate(FacesContext facesContext, UIComponent component,
Object value) throws ValidatorException {
UIInput dateIniComponent = (UIInput) component.getAttributes().get("dateIniComponent");
UIInput dateFinComponent = (UIInput) component.getAttributes().get("dateFinComponent");
String range = ((String) component.getAttributes().get("range")).toLowerCase();
String reference = ((String) component.getAttributes().get("reference")).toLowerCase();
if (value == null) {
return;
} else if (value instanceof Date) {
Date dateIni = null;
Date dateFin = null;
if ((dateIniComponent == null) && (dateFinComponent != null)) {
if (!dateFinComponent.isValid()) {
return; //No hay datos contra quien comparar
}
dateFin = (Date) dateFinComponent.getValue();
dateIni = (Date) value;
}
if ((dateFinComponent == null) && (dateIniComponent != null)) {
if (!dateIniComponent.isValid()) {
return; //No hay datos contra quien comparar
}
dateIni = (Date) dateIniComponent.getValue();
dateFin = (Date) value;
}
if ((dateIni != null) && (dateFin != null)) {
Calendar cal = Calendar.getInstance();
cal.setTime(dateIni);
Integer yearIni = cal.get(Calendar.YEAR);
Integer monthIni = cal.get(Calendar.MONTH);
Long daysMonthIni = (long) YearMonth.of(yearIni, monthIni + 1).lengthOfMonth();
Long daysYearIni = (long) cal.getActualMaximum(Calendar.DAY_OF_YEAR);
cal.setTime(dateFin);
Integer yearFin = cal.get(Calendar.YEAR);
Integer monthFin = cal.get(Calendar.MONTH);
Long daysMonthFin = (long) YearMonth.of(yearFin, monthFin + 1).lengthOfMonth();
Long daysYearFin = (long) cal.getActualMaximum(Calendar.DAY_OF_YEAR);
Long daysAllowed =
("year".equals(range) ? ("ini".equals(reference)?daysYearIni:("fin".equals(reference)?daysYearFin:null)) :
("month".equals(range) ? ("ini".equals(reference)?daysMonthIni:("fin".equals(reference)?daysMonthFin:null)) :
("week".equals(range) ? 7 : null)));
Long daysBetweenDates = TimeUnit.DAYS.convert(dateFin.getTime() - dateIni.getTime(), TimeUnit.MILLISECONDS);
if (daysAllowed == null) {
FacesMessage facesMessage
= new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Rango de fechas mal expresado en el facelet (vista) ",
"Rango de fechas mal expresado en el facelet (vista) ");
throw new ValidatorException(facesMessage);
}
if (dateFin.before(dateIni)) {
FacesMessage facesMessage
= new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Fecha Final No es posterior a Fecha Inicial ",
"La Fecha Final debe ser posterior a Fecha Inicial");
throw new ValidatorException(facesMessage);
}
if (daysBetweenDates > daysAllowed) {
FacesMessage facesMessage
= new FacesMessage(
FacesMessage.SEVERITY_ERROR,
"Se ha excedido los dias permitidos " + daysAllowed + " entre fechas seleccionadas, entre las fechas hay " + daysBetweenDates + " dias",
"entre las fechas hay " + daysBetweenDates + " dias");
throw new ValidatorException(facesMessage);
}
}
}
}
}
Now in the view
<p:outputLabel value="Date Initial:" for="itHeadDateInitial" />
<p:calendar id="itHeadDateInitial"
navigator="true"
binding="#{bindingDateIniComponent}"
value="#{theBean.DateIni}"
pattern="dd-MM-yyyy" mask="true" >
<f:validator validatorId="dateRangeValidator" />
<f:attribute name="dateFinComponent" value="#{bindingDateFinComponent}" />
<f:attribute name="range" value="year" />
<f:attribute name="reference" value="ini" />
</p:calendar>
<p:outputLabel value="Date Final:" for="itHeadDateFinal" />
<p:calendar id="itHeadDateFinal"
navigator="true"
binding="#{bindingDateFinComponent}"
value="#{theBean.DateFin}"
pattern="dd-MM-yyyy" mask="true" >
<f:validator validatorId="dateRangeValidator" />
<f:attribute name="dateIniComponent" value="#{bindingDateIniComponent}" />
<f:attribute name="range" value="year" />
<f:attribute name="reference" value="ini" />
</p:calendar>
Related
I have two , when there is a change in the selected option in the first select it must fire up an update in the second Select but it is only happening with page load.
Cliente.xhtml
<h:selectOneMenu id="ListaProvincia" immediate="true" value="#{beanCliente.provincia}">
<f:selectItems id="itemProvincia" value="#{beanCliente.listaProvincia}"/>
<f:ajax listener="beanCliente.changeCanton()" render="Canton" />
</h:selectOneMenu>
<h:outputLabel styleClass="optional">Canton</h:outputLabel>
<h:selectOneMenu id="Canton" value="#{beanCliente.canton}">
<f:selectItems value="#{beanCliente.listaCantones}"/>
</h:selectOneMenu>
beanCliente
public LinkedList getListaCantones() {
return this.listaCantones;
}
public void setDistrito(int Distrito) {
this.Distrito = Distrito;
}
public int getDistrito() {
return Distrito;
}
public void setListaDistrito()throws SNMPExceptions, SQLException {
String nombre="";
int id_distrito=0;
DistritoDB objDB = new DistritoDB();
LinkedList resultList = new LinkedList();
for (Iterator iter = objDB.listaDistritos(this.getCanton()).iterator(); iter.hasNext();) {
Distrito di = (Distrito) iter.next();
id_distrito= di.getID_Distrito();
nombre=di.getNombre();
resultList.add(new SelectItem(id_distrito, nombre));
}
this.listaCantones = resultList;
}
I'm trying to recuperate a list of data from my DB that depends on the date "datJourCchn" ;
I cloudn't find where is the error;
PS: I changed the Date imports to java.sql.Date instead of java.util.Date
here is my code:
CoursChangeDaoImpl
#Override
public List<CoursChange> getAllCoursParDate(Date datJourCchn)
{
System.out.println("date------------->" + datJourCchn);
List<CoursChange> coursChanges =new ArrayList<CoursChange>();
Devise devise =new Devise();
Connection conn = null;
String sql=
"SELECT d.LIB_DEV_DEV , d.LIB_SIGL_DEV , c.DAT_JOUR_CCHN , c.COD_ETAT_CCHN , c.MONT_CABA_CCHN , c.MONT_CABC_CCHN , c.MONT_CVBA_CCHN , c.MONT_CVBC_CCHN ,c.TIME_CCHN ,c.ID_COURS_CHANGE FROM COURS_CHANGE c , DEVISE d where c.COD_DEV_DEV=d.COD_DEV_DEV and c.DAT_JOUR_CCHN=?";
System.out.println("date------------->" + datJourCchn);
try
{
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDate(1, datJourCchn);
ResultSet rs=ps.executeQuery();
while(rs.next())
{ CoursChange coursChange =new CoursChange();
coursChange.setIdCoursChange(rs.getInt("ID_COURS_CHANGE"));
coursChange.setCodEtatCchn(rs.getString("COD_ETAT_CCHN"));
coursChange.setMontCabaCchn(rs.getFloat("MONT_CABA_CCHN"));
coursChange.setMontCabcCchn(rs.getFloat("MONT_CABC_CCHN"));
coursChange.setMontCvbaCchn(rs.getFloat("MONT_CVBA_CCHN"));
coursChange.setMontCvbcCchn(rs.getFloat("MONT_CVBC_CCHN"));
devise.setLibDevDev(rs.getString("LIB_DEV_DEV"));
devise.setLibSiglDev(rs.getString("LIB_SIGL_DEV"));
coursChange.setDatJourCchn(rs.getDate("DAT_JOUR_CCHN"));
coursChange.setTimeCchn(rs.getString("TIME_CCHN"));
System.out.println(rs.getInt("ID_COURS_CHANGE"));
System.out.println(rs.getString("LIB_DEV_DEV"));
coursChanges.add(coursChange );
}
rs.close();
ps.close();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
finally
{
try
{
conn.close();
}
catch(SQLException e)
{
}
}
return coursChanges;
}
page.xhtml
<p:calendar id="button" value="#{coursChangeCtr.datJourCchn}"
showOn="button" pattern="MM-dd-yy">
</p:calendar>
<p:commandButton value="submit"
action="#{coursChangeCtr.listCoursPreparedStatement()}" update="AjoutTab" />
<p:dataTable value="#{coursChangeCtr.listCoursPreparedStatement()}" var="cours"
id="AjoutTab" emptyMessage="aucune info!!" rows="5"
style="width:1000px;font-size:13px;margin-left: 25px">
the error is:
Cannot convert 17/04/16 00:00 of type class java.util.Date to class java.sql.Date
Instead of
ps.setDate(1, datJourCchn);
you can use
ps.setDate(1, new java.sql.Date(datJourCchn.getTime()));
or, if datJourCchn might be null
if (datJourCchn == null) {
s.setNull(1, java.sql.Types.DATE);
} else {
s.setDate(1, new java.sql.Date(datJourCchn.getTime()));
}
i have a String like "Hello [everyone]!". "Everyone" should be replaced with a commandLink that points to an Page-Object.
For Example:
My Code detects "[everyone]" and creates a new "Page" with the headline "everyone" in my JavaDB Database. Now i want that [everyone] will shown as an commandLink:
Hello <h:commandLink value="everyone" action="#{PageController.getPage(everyone)}" />
or something else.
ATM i have this code to display the text with the []-Tags:
<h:outputText value="#{PageController.currentPage.latestContent.text}" />
Now what is the best practise to replace Tags (i.e. [XYZ]) with a specific commandLink? Or rather: how i can replace substrings with JSF-Tags (they should be rendered)? I have only found the possibility to create converter, but only examples to convert the complete string. :/ To find out the right substring i use Regulary Expressions.
Merry Christmas :)
My Soluion:
#ApplicationScoped
#Named("TagViewPhase")
public class TagViewPhase implements Serializable {
Application app;
public void beforePhase(PhaseEvent event) {
if (app == null) {
app = event.getFacesContext().getApplication();
}
if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
scanForPageContent(event.getFacesContext().getViewRoot());
}
}
private void scanForPageContent(UIComponent component) {
for (UIComponent i : component.getChildren()) {
if ("pageContent".equals(i.getId())) {
HtmlOutputText content = (HtmlOutputText) i;
HtmlPanelGroup group = generatePageContent(content);
content.getParent().getChildren().add(group);
content.getParent().getChildren().remove(i);
} else {
scanForPageContent(i);
}
}
}
private HtmlPanelGroup generatePageContent(final HtmlOutputText pContent) {
List<UIComponent> childTree = new ArrayList<>();
String content = pContent.getValue().toString();
Pattern pattern = Pattern.compile("\\[(.*?)\\]");
Matcher matcher = pattern.matcher(content);
Integer start, end = 0;
String linkValue;
while (matcher.find()) {
start = matcher.start();
if (end < start) {
HtmlOutputText htmlText = (HtmlOutputText) app.createComponent(HtmlOutputText.COMPONENT_TYPE);
htmlText.setValue(content.substring(end, start));
childTree.add(htmlText);
}
end = matcher.end();
linkValue = content.substring(start + 1, end - 1);
HtmlCommandLink link = (HtmlCommandLink) app.createComponent(HtmlCommandLink.COMPONENT_TYPE);
link.setValue(linkValue);
link.setActionExpression(JSFExpression.createMethodExpression(
"#{PageController.switchPageByString('" + linkValue + "')}",
String.class,
new Class<?>[]{}
));
childTree.add(link);
}
if (end < content.length()) {
HtmlOutputText htmlText = (HtmlOutputText) app.createComponent(HtmlOutputText.COMPONENT_TYPE);
htmlText.setValue(content.substring(end, content.length()));
childTree.add(htmlText);
}
HtmlPanelGroup group = (HtmlPanelGroup) app.createComponent(HtmlPanelGroup.COMPONENT_TYPE);
group.getChildren().addAll(childTree);
return group;
}
}
xhtml File:
<f:view beforePhase="#{TagViewPhase.beforePhase}">
<h:panelGroup>
<h:outputText id="pageContent" value="#{PageController.currentPageContent.text}" />
</h:panelGroup>
</f:view>
I have an implementation of lazyDataModel following this tutorial http://uaihebert.com/?p=1120
My code its a bit different from this tutorial, here it is:
View:
<p:dataTable id="tablaClientes" value="#{AgendaManualMBean.allClientes}"
var="tablaClientes"
widgetVar="clienteTable"
rowKey="#{tablaClientes.clDocid}"
selection="#{AgendaManualMBean.ciatt001}" selectionMode="single" rows="10"
lazy="true" paginatorPosition="top"
paginatorTemplate="{RowsPerPageDropdown}{FirstPageLink}{PreviousPageLink}
{CurrentPageReport} {NextPageLink} {LastPageLink}" rowsPerPageTemplate="5,10,15"
emptyMessage="No existen clientes">
<f:facet name="header" >
<p:outputPanel>
<h:outputText value="Busqueda " />
<p:inputText id="globalFilter" onkeyup="clienteTable.filter()"
style="width:150px" size="10"/>
</p:outputPanel>
</f:facet>
<p:column id="numOrdenColumn" filterBy="#{tablaClientes.clDocid}"
width="50"
headerText="Identificación"
filterMatchMode="contains">
<h:outputText value="#{tablaClientes.clDocid}" />
</p:column>
<p:column id="nomCliColumn"
filterBy="#{tablaClientes.clNombre1}"
width="250"
headerText="Cliente"
filterMatchMode="contains">
<h:outputText value="#{tablaClientes.clNombre1}" />
</p:column>
</p:dataTable>
MY Managed Bean:
public LazyDataModel<Ciatt001> getAllClientes() {
if (listaClientesLazy == null) {
listaClientesLazy = new LazyClienteModel(ambiente.getCodCia(),ambiente.getCodAge());
//listaClientesLazy = new LazyClienteModelMBean();
}
return listaClientesLazy;
}
My LazyDataModel
public List<Ciatt001> load(int startingAt, int maxPerPage, String sortField, SortOrder sortOrder, Map<String, String> filters) {
String a = "";
try {
listaClientes = new ArrayList<Ciatt001>();
a = String.valueOf(agendamientoSession.countClientes2(cia, age));
listaClientes = agendamientoSession.listaClientes2(cia, age, startingAt, maxPerPage);
} catch (Exception e) {
e.printStackTrace();
}
if (getRowCount() <= 0) {
setRowCount(Integer.parseInt(a));
}
setPageSize(maxPerPage);
return listaClientes;
}
#Override
public Object getRowKey(Ciatt001 ciatt001) {
return ciatt001.getClDocid();
}
#Override
public Ciatt001 getRowData(String docid) {
//Integer id = Integer.valueOf(idBandeja);
for (Ciatt001 ciatt001 : listaClientes) {
if (docid.equals(ciatt001.getClDocid())) {
return ciatt001;
}
}
return null;
}
And the ejb methods:
public List<Ciatt001> listaClientes2(String cia, String age ,int startingAt, int maxPerPage) {
Query query = em.createNamedQuery("Ciatt001.listaClientesPorCiaPorAge2");
query.setParameter(1, cia);
query.setParameter(2, age);
query.setFirstResult(startingAt);
query.setMaxResults(maxPerPage);
return query.getResultList();
}
public String countClientes2(String cia, String age) {
Query query = em.createNamedQuery("Ciatt001.countClientes2");
query.setParameter(1, cia);
query.setParameter(2, age);
return query.getSingleResult().toString();
}
How can I achieve a global filter using this lazy loading implementation?
On load functon, get the filter value (check if is not null).
String filterValue = filters.get("globalFilter");
Then if you are not using any others filters ,make a query using disjunction (OR):
"select * from table where x = ? OR y = ?" //replace ? for globalFilter value
If you are using others fields, you should do:
//normal filters
filtersCondition = "(x = ? AND y = ?)" //replace ? for filters values
//global filters
globalFilterCondition = "(x = globalFilter OR y = globalFilter)" //replace ? for globalFilter value
//normal filters + global filter
query = "select * from table where " +filtersCondition+ " AND "+ globalFilterCondition
Of course, this queries are just an example , you should build a nice one and well parameterized =)
This how i get it to work , pagination and filtering :D
The LazyDataModel look like this now:
in the load method:
Set set = filters.entrySet();
Iterator i = set.iterator();
if (i.hasNext()) {
Map.Entry me = (Map.Entry) i.next();
filterValue = (String) me.getValue();
}
a = String.valueOf(agendamientoSession.countClientes2(cia, age, filterValue));
listaClientes = agendamientoSession.listaClientes2(cia, age, startingAt, maxPerPage, filterValue);
setRowCount(Integer.parseInt(a));
the ejb :
public List<Ciatt001> listaClientes2(String cia, String age ,int startingAt, int maxPerPage,String filtro) {
Query query = em.createNamedQuery("Ciatt001.listaClientesPorCiaPorAge2");
query.setParameter(1, cia);
query.setParameter(2, age);
query.setParameter(3,"%"+filtro+"%");
query.setParameter(4,"%"+filtro+"%");
query.setFirstResult(startingAt);
query.setMaxResults(maxPerPage);
}
and my query:
select * from ciatt001 c where c.cl_co_cia=? and c.cl_ag_agencia =?
and c.cl_estado='A' and (c.cl_docid like ? or c.cl_nombre1 like ? .. more filters if needed)
thats all,
now wil be looking for the sort implementation :D
i need to validate multiple fields in the form, individual field validations are working fine.
i wanted to know if all the fields are empty is there any way with jsf 1.x and richfaces 3.3 to display a single message like "all fields are mandatory" instead of each validation message
you can have one list that contains all the error messages, and the list gets populated onsubmit.
example:
public String doBid() {
boolean flag=false;
errorMessages = new ArrayList<String>();
if (getUserID().equals("")) {
flag=true;
}
if (getKeyword().equals("")) {
flag=true;
}
if (getNumericBidAmount() == 0.00) {
flag=true;
}
if (getNumericBidDuration() =0) {
flag=true;
}
if (flag==true)
errorMessages.add("all fields are mandatory");
if (errorMessages.size() > 0) {
return(null);
} else {
return("success");
}
14 }
---------
public String getErrorMessages() {
String messageList;
if ((errorMessages == null) ||
(errorMessages.size() == 0)) {
messageList = "";
} else {
messageList = "<FONT COLOR=RED><B><UL>\n";
for(String message: errorMessages) {
messageList = messageList + "<LI>" + message + "\n";
}
messageList = messageList + "</UL></B></FONT>\n";
}
return(messageList);
}
--------------------
<h:form>
<h:outputText value="#{bidBean1.errorMessages}"
escape="false"/>
<TABLE>
<TR>
<TD>User ID:
<h:inputText value="#{bidBean1.userID}"/></TD></TR>
<TR>
<TD>Keyword:
<h:inputText value="#{bidBean1.keyword}"/></TD></TR>
<TR>
<TD>Bid Amount:
$<h:inputText value="#{bidBean1.bidAmount}"/></TD></TR>
<TR>
<TD>Duration:
<h:inputText value="#{bidBean1.bidDuration}"/></TD></TR>
<TR><TH>
<h:commandButton value="Send Bid!"
action="#{bidBean1.doBid}"/></TH></TR>
</TABLE>
</h:form>
I'm using MyFaces Extensions Validator + a simple plugin with a ProcessedInformationRecorder for doing exactly what you are talking about.