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...