我注意到有人问过这个问题,但是没有正确回答。
我有一个数据表,其中有两列 开始日期 和 结束日期 。两者都包含总理字符p:calendar控件。我需要确保每一行中column1中的日期都不晚于column2中的日期。我想将其绑定到JSF验证框架中,但是遇到了麻烦。
我尝试将数据表标记为rowStatePreserved =“ true”,这使我能够获取值,但是还是有些错误,因为当它失败时,第一行中的所有值都会覆盖所有其他值。我在做什么错,还是应该使用完全不同的策略?
xhtml代码
<h:form> <f:event type="postValidate" listener="#{bean.doCrossFieldValidation}"/> <p:dataTable id="eventDaysTable" value="#{course.courseSchedules}" var="_eventDay" styleClass="compactDataTable" > <p:column id="eventDayStartColumn"> <f:facet name="header"> Start </f:facet> <p:calendar id="startDate" required="true" value="#{_eventDay.startTime}" pattern="MM/dd/yyyy hh:mm a"/> </p:column> <p:column id="eventDayEndColumn"> <f:facet name="header"> End </f:facet> <p:calendar id="endDate" required="true" value="#{_eventDay.endTime}" pattern="MM/dd/yyyy hh:mm a"/> </p:column> </p:dataTable> </h:form>
验证码
public void doCrossFieldValidation(ComponentSystemEvent cse) { UIData eventsDaysStable = (UIData) cse.getComponent().findComponent("eventDaysTable"); if (null != eventsDaysStable && eventsDaysStable.isRendered()) { Iterator<UIComponent> startDateCalendarIterator = eventsDaysStable.findComponent("eventDayStartColumn").getChildren().iterator(); Iterator<UIComponent> endDateCalendarIterator = eventsDaysStable.findComponent("eventDayEndColumn").getChildren().iterator(); while (startDateCalendarIterator.hasNext() && endDateCalendarIterator.hasNext()) { org.primefaces.component.calendar.Calendar startDateComponent = (org.primefaces.component.calendar.Calendar) startDateCalendarIterator.next(); org.primefaces.component.calendar.Calendar endDateComponent = (org.primefaces.component.calendar.Calendar) endDateCalendarIterator.next(); Date startDate = (Date) startDateComponent.getValue(); Date endDate = (Date) endDateComponent.getValue(); if (null != startDate && null != endDate && startDate.after(endDate)) { eventScheduleChronologyOk = false; startDateComponent.setValid(false); endDateComponent.setValid(false); } } if (!eventScheduleChronologyOk) { showErrorMessage(ProductManagementMessage.PRODUCT_SCHEDULE_OUT_OF_ORDER); } } }
我究竟做错了什么
以非标准的JSF方式在数据表的上下文之外执行验证。仅 当 您(或JSF)在数据表上迭代时,行数据才可用。实际上<p:calendar>,每列中只有一个组件具有多个不同的状态,具体取决于当前的数据表迭代回合。当您不遍历数据表时,这些状态不可用。然后,您只会获得null价值。
<p:calendar>
null
从技术上讲,到目前为止,您使用不同的验证方法,您应该visitTree()在UIData组件上调用方法并在VisitCallback实现中执行工作。这将遍历数据表。
visitTree()
UIData
VisitCallback
例如,
dataTable.visitTree(VisitContext.createVisitContext(), new VisitCallback() { @Override public VisitResult visit(VisitContext context, UIComponent component) { // Check if component is instance of <p:calendar> and collect its value by its ID. return VisitResult.ACCEPT; } });
这只是笨拙。这为您提供了每一行,您需要自己维护和检查行索引并收集值。注意,调用UIInput#setValid()也应该在实现内部完成VisitCallback。
UIInput#setValid()
还是我应该使用完全不同的策略?
是的,请使用标准Validator的标准JSF方法。您可以将一个组件作为另一组件的属性来传递。
Validator
例如
<p:column> <p:calendar binding="#{startDateComponent}" id="startDate" required="true" value="#{item.start}" pattern="MM/dd/yyyy hh:mm a"/> </p:column> <p:column > <p:calendar id="endDate" required="true" value="#{item.end}" pattern="MM/dd/yyyy hh:mm a"> <f:validator validatorId="dateRangeValidator" /> <f:attribute name="startDateComponent" value="#{startDateComponent}" /> </p:calendar> </p:column>
与
@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)); } } }
由于两个组件都在同一行中,因此每次调用此验证器时,startDateComponentwill都会自动“自动”提供正确的值getValue()。请注意,该验证器在数据表外部也可以重用,而您的初始方法则不是。
startDateComponent
getValue()
或者,您可以将OmniFaces <o:validateOrder>用作完整的解决方案。它的展示实例甚至展示<p:calendar>了<p:dataTable>。
<o:validateOrder>
<p:dataTable>