2012-06-21 25 views
11

我注意到這個問題被問到,但它沒有被正確回答。在jsf h中交叉字段驗證:datatable使用p:日曆

我有一個數據表有兩列開始日期結束日期。它們都包含primefaces p:日曆控件。我需要確保列1中的日期不在第2列中的日期之後。我想將它與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> 

validationCode

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); 
      } 

     } 

    } 

回答

17

我在做什麼DataTable的上下文之外錯

執行驗證的非標準JSF辦法。行數據只有,而你(或JSF)迭代數據表。根據當前可數據迭代循環,每個列中只有一個<p:calendar>組件,它具有多個不同的狀態。這些狀態在您未迭代數據表時不可用。那麼你只會得到null作爲價值。

技術上,與你不同的驗證方法,到目前爲止,你應該調用UIData組件上visitTree()方法,並在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; 
    } 
}); 

這僅僅是笨拙。這給你每一行,你需要維護並檢查你自己的行索引並收集值。請注意,在VisitCallback實現中也應該調用UIInput#setValid()


或者我應該用一種完全不同的策略?

是的,使用標準的JSF方式正常的Validator。您可以將一個組件作爲另一個組件的屬性傳遞。

E.g.

<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)); 
     } 
    } 

} 

由於兩個部件是在同一行中,startDateComponent將「自動地」給予正確的值回getValue()每次這個驗證被調用。請注意,此驗證程序在數據表之外也可重用,而您的初始方法則不可用。

或者,您可以使用OmniFaces<o:validateOrder>作爲完整的解決方案。它的showcase示例甚至在<p:dataTable>內展示了<p:calendar>組件的這個特定用例。

+2

正是我所需要的 –

+0

不客氣。 – BalusC

+0

@BalusC驗證跨領域和跨行驗證的策略應該是什麼? –