Confirm the order of elements inside ui: repeat

I am writing a validation method in JSF 2. I have a ui:repeat element on my page that displays a list of elements. Each element has a date property, and I need the dates to match each other in a specific sequence, for example. the date of the last item in the list does not come before the date of the first item. I tried to get all the children inside ui: iterate over and iterate over them for comparison, but I don't know where to start. I saw how to get a specific item by ID:

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

However, within ui:repeat , the identifier values ​​become unique to JSF, so I don't know what they are at compile time. Also, at compile time, I don't know how many items will be in the list.

I looked through the Javadoc for UIViewRoot and other related classes, and tried a couple of things, but I get errors, everything doesn't work, and I really don't know, even close to getting anywhere. I leave the code for my attempts from this post because they are probably a joke.

+3
validation jsf uirepeat
source share
1 answer

There, physically, only the component is one UIInput , the state of which changes depending on the current round of iteration of the UIRepeat . It is accessible only by its client identifier without the UIRepeat : findComponent("formId:inputId") index (the UIRepeat index only matters on the client side). However, when a component is programmatically accessible outside the UIRepeat context UIRepeat this way, it does return a seemingly empty state.

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

Here is an example run:

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

Using this method validateOrder() (again, just an example run, this approach naively assumes that there is only one UIInput component in the 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!")); } } 

Notice that he visits the tree twice; the first time to collect values ​​and the second time to mark these inputs incorrect. Also note that this very specific requirement cannot be met with the standard JSF validator. You cannot attach <f:validator> to <ui:repeat> . It is theoretically possible to attach it to <h:inputText> , but this will lead to the fact that the same validator will work as many times as the number of repeating elements, which makes no sense. In addition, the validator will have to consider getSubmittedValue() vs getValue() in this way.

OmniFaces has a <o:validateOrder> component that does the same thing on fixed components, but is not intended to be used in dynamically repeating components.

+4
source share

All Articles