我想比较两个日期(StartDate和EndDate)并检查一个日期是否在另一个日期之前。最简单的解决方案就是在支持bean和"短路"方法。
但是,此验证不会与其他表单验证同时发生。例如,如果我有另一个需要验证的字段(除了日期)并且输入无效,我将只收到该特定字段的消息。只有在其他字段有效的情况下才能根据辅助bean获得日期验证。
任何人都有解决方案吗?
答案 0 :(得分:34)
但是,此验证不会与其他表单验证同时发生。
支持bean操作方法确实不打算执行输入验证。
任何人都有解决方案吗?
使用合适的工具完成工作;使用正常的Validator
。
@FacesValidator("dataRangeValidator")
public class DateRangeValidator implements Validator {
// ...
}
使用单个验证器验证多个输入值反过来确实是一个故事。基本上,您应该抓取/传递其他组件或其值到validate()
方法实现中。在最简单的形式中,您可以使用<f:attribute>
。假设您使用<p:calendar>
来选择日期,这是一个具体的启动示例:
<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>
(注意binding
属性,它使EL范围内的组件完全在给定的变量名称上;还要注意这个例子是原样的,你绝对不应该将它绑定到bean属性!)
dateRangeValidator
看起来像这样:
@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));
}
}
}
如果您碰巧使用JSF utility library OmniFaces,那么您也可以使用<o:validateOrder>
component。无需自定义验证器即可满足以下要求:
<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" />
答案 1 :(得分:5)
如果您使用的PrimeFaces可以限制最短和最长日期。用户无法选择更大的范围这是一个例子:
<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>
答案 2 :(得分:1)
由于BalusC解决方案只有在表单上有一个要验证的日期范围时才有效,以下是允许多个日期范围验证的改进:
将另一个<f:attribute>
添加到endDate日历组件,您可以在其中为startDate组件指定绑定属性名称:
<f:attribute name="bindingAttributeName" value="startDateComponent" />
然后在验证器中:
String startDateBindingAttrName = (String) component.getAttributes().get("bindingAttributeName");
UIInput startDateComponent = (UIInput) component.getAttributes().get(startDateBindingAttrName);
答案 3 :(得分:1)
像Base一样采用Answer BalusC,供我将来使用...
这可以设置时间间隔(年,月,周),参考比较初始值或最终值。
@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);
}
}
}
}
}
现在在视图中
<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>