4

我正在 JSF 2 中编写一个验证器方法。ui:repeat我的页面中有一个元素可以呈现项目列表。每个项目都有一个日期属性,我需要确保日期以特定顺序相互对应,例如列表中最后一项的日期不早于第一项的日期。我试图获取 ui:repeat 中的所有子元素并对其进行迭代以进行比较,但我真的不知道从哪里开始。我已经看到如何通过 ID 获取特定元素:

UIInput input = (UIInput) context.getViewRoot().findComponent(elementId);

但是,在ui:repeatID 值中,JSF 是唯一的,所以我不知道它们在编译时是什么。此外,在编译时我不知道列表中有多少项。

我查看了 Javadoc forUIViewRoot和其他相关类,并尝试了几件事,但我遇到了错误,事情不工作,而且我真的不知道我是否接近任何地方。我将我尝试的代码从这篇文章中删除,因为它们可能是个笑话。

4

1 回答 1

6

物理上只有一个 UIInput组件的状态会根据UIRepeat. 它仅通过其客户端 ID 而不带UIRepeat索引可用:(findComponent("formId:inputId")索引UIRepeat仅在客户端有意义)。但是,当在这种方式的上下文之外以编程方式访问组件时UIRepeat,它确实会返回一个看似空的状态。

为了UIInput在所有这些状态下访问组件UIRepeat并收集它们的值,您需要UIComponent#visitTree()UIRepeat.

这是一个启动示例:

<ui:repeat value="#{bean.items}" var="item">
    <f:event type="postValidate" listener="#{bean.validateOrder}" />
    <h:inputText value="#{item.value}" />
</ui:repeat>

使用这种validateOrder()方法(同样,只是一个启动示例,这种方法天真地假设转发器中只有一个UIInput组件):

@SuppressWarnings("rawtypes")
public void validateOrder(ComponentSystemEvent event) {
    final FacesContext context = FacesContext.getCurrentInstance();
    final List<Comparable> values = new ArrayList<Comparable>();

    event.getComponent().visitTree(VisitContext.createVisitContext(context), new VisitCallback() {
        @Override
        public VisitResult visit(VisitContext context, UIComponent target) {
            if (target instanceof UIInput) {
                values.add((Comparable) ((UIInput) target).getValue());
            }
            return VisitResult.ACCEPT;
        }
    });

    boolean ordered = new ArrayList<Comparable>(new TreeSet<Comparable>(values)).equals(values);

    if (!ordered) {
        event.getComponent().visitTree(VisitContext.createVisitContext(context), new VisitCallback() {
            @Override
            public VisitResult visit(VisitContext context, UIComponent target) {
                if (target instanceof UIInput) {
                    ((UIInput) target).setValid(false);
                }
                return VisitResult.ACCEPT;
            }
        });

        context.validationFailed();
        context.addMessage(null, new FacesMessage("Values are not in order!"));
    }
}

请注意,它会访问树两次;第一次收集值,第二次将这些输入标记为无效。还要注意,这个非常具体的要求不能用标准的 JSF 验证器来完成。你不能附加一个<f:validator>on <ui:repeat>。附加它在<h:inputText>理论上是可能的,但它会导致同一个验证器运行的次数与重复项的数量一样多,这是没有意义的。此外,验证者需要以这种方式考虑getSubmittedValue()vs。getValue()

OmniFaces 有一个<o:validateOrder>组件,它在固定组件上做类似的事情,但它不是为在动态重复组件中使用而设计的。

于 2013-08-27T15:55:19.130 回答