2012-06-21 7 views
11

この質問は尋ねられましたが、正しく回答されていません。jsfのクロスフィールド検証h:p:calendarを使用したデータテーブル

私は2つの列開始日終了日を持つデータテーブルを持っています。どちらも、primeface p:カレンダーコントロールを含んでいます。私は、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> 

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

私が間違って非標準JSFでのDataTableのコンテキスト外で検証を実行

何をやっています方法。行データはしか使用できませんが、(またはJSFは)データテーブルを反復処理しています。物理的には、すべての列に1つの<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; 
    } 
}); 

これは不器用です。これにより、すべての単一の行が得られます。行インデックスを維持してチェックし、値を収集する必要があります。 UIInput#setValid()を呼び出すことは、VisitCallback実装内で行う必要があります。


または私は完全に異なる戦略を使用すべきですか?

はい、通常のValidatorの標準JSF形式を使用してください。一方のコンポーネントを他方のコンポーネントの属性として渡すことができます。

など。

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

} 

<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>           

両成分が同じ行にあるように、startDateComponentは「自動的」このバリデータが呼び出されたバックgetValue()たびに正しい値を与えます。このバリデータは、初期の方法ではなく、データテーブルの外部で再利用可能であることに注意してください。

また、OmniFaces<o:validateOrder>を完全なソリューションとして使用することもできます。そのshowcaseの例では、<p:dataTable>の中に<p:calendar>のコンポーネントのこの特定の使用例が示されています。

+2

私が必要としたもの –

+0

あなたは大歓迎です。 – BalusC

+0

@BalusCクロスフィールド検証とクロスロー検証を検証する戦略は何ですか? –

関連する問題