Seam validator for live duplicate validation
asookazian Jul 21, 2009 5:51 AMI have three HtmlSelectOneMenu components (say Foo 1, Foo 2, Foo 3).
I want to implement live vaildation using a Seam validator (see below for example from Seam ref doc) to warn user if they select an already inputted value for another instance of the Foo drop-down control. For example, if they select Best Buy
for Foo 1 and Best Buy
for Foo 2 and then tab out of Foo 2 field, I want to display an error msg like duplicates not allowed
on Foo 2 field. I want this to happen for onblur event (a4j:support) as well as on submission of the form. I also want to show warning msg when user enters a value that is already saved to the table for this entity (but that's another problem altogether really and not relevant to this thread).
@Name("itemValidator") @BypassInterceptors @org.jboss.seam.annotations.faces.Validator public class ItemValidator implements javax.faces.validator.Validator { public void validate(FacesContext context, UIComponent cmp, Object value) throws ValidatorException { ItemController ItemController = (ItemController) Component.getInstance("itemController"); boolean valid = itemController.validate(value); if (!valid) { throw ValidatorException("Invalid value " + value); } } } <h:inputText value="#{shop.item}" validator="itemValidator" />
What is the best way to implement this?
The problem I'm having is that in the validate() method below, I have access to only the value of the current field, which is the field the focus was on when the onblur event fired. I need to have access to the values for all three HtmlSelectOneMenu components in the same method. I tried to inject the backing bean into my FooValidator class but that only gives me null references to the HtmlSelectOneMenu components.
So I found this code from this link:
public static UIComponent findComponentInRoot(String id) { UIComponent component = null; FacesContext facesContext = FacesContext.getCurrentInstance(); if (facesContext != null) { UIComponent root = facesContext.getViewRoot(); component = findComponent(root, id); } return component; } public static UIComponent findComponent(UIComponent base, String id) { if (id.equals(base.getId())) return base; UIComponent kid = null; UIComponent result = null; Iterator kids = base.getFacetsAndChildren(); while (kids.hasNext() && (result == null)) { kid = (UIComponent) kids.next(); if (id.equals(kid.getId())) { result = kid; break; } result = findComponent(kid, id); if (result != null) { break; } } return result; }
But I'm not sure if this is even a best practice approved
approach to solve this problem for a JSF/Seam app. It returns HtmlSelectOneMenu as the UIComponent instance but I really want the underlying entity class (I'm using the <s:convertEntity> tag) so I can exec the relevant getter method on the entity class to have the data I need to compare for duplicates.
Any tips would be appreciated. This use case (validation of multiple replicated input fields in a JSF form) is not very common in Seam apps that I've seen so far...