Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
378 views
in Technique[技术] by (71.8m points)

validation - Validate order of items inside ui:repeat

I'm writing a validator method in JSF 2. I have a ui:repeat element in my page that renders a list of items. Each item has a date property, and I need to ensure the dates correspond to each other in a specific sequence, e.g. the date of the last item in the list doesn't come before the date of the first item. I was trying to get all the child elements inside the ui:repeat and iterate over them to do the comparison, but I don't really know where to start. I've seen how to get a specific element by ID:

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

However, within the ui:repeat the ID values are made unique by JSF, so I don't know what they are at compile time. Furthermore, at compile time I don't know how many items there will be in the list.

I've looked at the Javadoc for UIViewRoot and other associated classes, and have tried a couple things, but I'm getting errors, things aren't working, and I don't really know if I'm even close to getting anywhere. I'm leaving the code of my attempts out of this post, becuase they're probably a joke.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There's physically only one UIInput component whose state changes depending on the current iteration round of UIRepeat. It's available by just its client ID without the UIRepeat index: findComponent("formId:inputId") (the UIRepeat index is only of significance in the client side). However, when the component is programmatically been accessed outside the context of UIRepeat this way, then it'll indeed return a seemingly empty state.

In order to visit the UIInput component in all those states as they are inside the UIRepeat and collect their values, you need to run UIComponent#visitTree() on the UIRepeat.

Here's a kickoff example:

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

With this validateOrder() method (again, just a kickoff example, this approach naively assumes that there's only one UIInput component in the repeater):

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

Note that it visits the tree twice; first time to collect the values and second time to mark those inputs invalid. Also note that this very specific requirement can't be done with a standard JSF validator. You can't attach a <f:validator> on <ui:repeat>. Attaching it on <h:inputText> is theoretically possible, but it would cause the very same validator to run as many times as the amount of repeated items, which doens't make sense. Also, the validator would need to take getSubmittedValue() vs getValue() into account this way.

OmniFaces has an <o:validateOrder> component which does a similar thing on fixed components, but it isn't designed for usage in dynamically repeated components.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...