物理上只有一个 UIInput 组件的状态会根据UIRepeat 的当前迭代轮次而改变。它仅通过其客户端 ID 可用,而没有 UIRepeat 索引:findComponent("formId:inputId")(UIRepeat 索引仅在客户端有意义)。但是,当以这种方式在UIRepeat 的上下文之外以编程方式访问组件时,它确实会返回一个看似空的状态。
为了在所有这些状态下访问UIInput 组件,因为它们在UIRepeat 内并收集它们的值,您需要在UIRepeat 上运行UIComponent#visitTree()。
这是一个启动示例:
<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 验证器来完成。您不能在<ui:repeat> 上附加<f:validator>。将它附加到<h:inputText> 理论上是可能的,但它会导致同一个验证器运行的次数与重复项的数量一样多,这是没有意义的。此外,验证器需要以这种方式考虑 getSubmittedValue() 与 getValue()。
OmniFaces 有一个 <o:validateOrder> 组件,它在固定组件上做类似的事情,但它不是为在动态重复组件中使用而设计的。