11

この質問がされていることに気付きましたが、正しく回答されていません。

start dateend dateの 2 つの列を持つデータテーブルがあります。どちらにもprimefaces p:calendar コントロールが含まれています。行ごとに、列 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>

検証コード

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

        }

    }
4

1 に答える 1

16

私は何を間違っているのですか

非標準のJSFの方法で、データテーブルのコンテキスト外で検証を実行します。行データは、データテーブルを反復処理している間(またはJSFが反復処理)にのみ使用できます。<p:calendar>現在のデータテーブルの反復ラウンドに応じて、複数の異なる状態を持つすべての列に物理的に1つのコンポーネントしかありません。データテーブルを反復処理していない場合、これらの状態は使用できません。その場合、値としてのみ取得nullされます。

技術的には、これまでのさまざまな検証アプローチ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


または、まったく異なる戦略を使用する必要がありますか?

はい、通常Validatorの標準JSF方法を使用します。1つのコンポーネントを他のコンポーネントの属性として渡すことができます。

例えば

<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>を完全なソリューションとして使用することもできます。そのショーケース<p:calendar>の例は、内のコンポーネントのこの特定のユースケースを示してい<p:dataTable>ます。

于 2012-06-25T16:11:13.360 に答える