使用p:calendar在jsf h:datatable中进行跨字段验证 [英] Cross field validation in jsf h:datatable using p:calendar

查看:144
本文介绍了使用p:calendar在jsf h:datatable中进行跨字段验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到有人问了这个问题,但没有正确回答.

I noticed this question was asked, but it has not been answered correctly.

我有一个数据表,其中有两列开始日期结束日期.两者都包含总理字符p:calendar控件.我需要确保每一行中column1中的日期都不晚于column2中的日期.我想将其绑定到JSF验证框架中,但是遇到了麻烦.

I have a datatable that has two columns start date and end date. Both contain primefaces p:calendar controls in them. I need to ensure that for each row that the date in column1 is not after the date in column2. I would like to tie this into the JSF validation framework, but I'm having trouble.

我尝试将数据表标记为rowStatePreserved ="true",这使我可以获取这些值,但是还是有些错误,因为当它失败时,第一行中的所有值都会覆盖所有其他值.我在做什么错,还是应该使用完全不同的策略?

i've tried marking the datatable rowStatePreserved="true" , this allows me to get the values, but something is still wrong as when it fails, all the values in the first row overwrite all the other values. What am I doing wrong, or should I be using a completely different strategy?

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

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

        }

    }

推荐答案

我在做什么错

以非标准的JSF方式在数据表的上下文之外执行验证.行数据仅在(或JSF)迭代数据表时可用.实际上,每列中只有一个<p:calendar>组件,根据当前的数据表迭代周期,该组件具有多个不同的状态.当您不遍历数据表时,这些状态不可用.然后,您只会得到null作为值.

Performing validation outside the context of the datatable in a non-standard JSF way. The row data is only available while you're (or JSF is) iterating over the datatable. There's physically only one <p:calendar> component in every column which has multiple different states depending on the current datatable iteration round. Those states are not available when you're not iterating over the datatable. You'll only get null as value then.

从技术上讲,到目前为止,您采用了不同的验证方法,因此应该调用

Technically, with your different validation approach so far, you should be invoking visitTree() method on the UIData component and performing the job in the VisitCallback implementation. This will iterate over the datatable.

例如,

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实现内部完成.

This is only clumsy. This gives you every single row, you'd need to maintain and check the row index youself and collect the values. Note that calling UIInput#setValid() should also be done inside the VisitCallback implementation.

还是应该使用完全不同的策略?

是的,使用标准JSF方式的普通Validator.您可以将一个组件作为另一个组件的属性来传递.

Yes, use a normal Validator the standard JSF way. You can pass the one component as an attribute of the other component.

例如

<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().请注意,当您的初始方法并非如此时,该验证器也可以在数据表外部重用.

As both components are in the same row, the startDateComponent will "automagically" give the right value back on getValue() everytime this validator is invoked. Note that this validator is also reuseable outside the datatable while your initial approach isn't.

或者,您可以使用 OmniFaces <o:validateOrder>作为完整的解决方案.其展示案例示例甚至展示了<p:dataTable><p:calendar>组件的这种特定用例.

Alternatively, you can use OmniFaces <o:validateOrder> as a complete solution. Its showcase example even shows off this specific use case of <p:calendar> components inside a <p:dataTable>.

这篇关于使用p:calendar在jsf h:datatable中进行跨字段验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆