Well, I do it as follows, and it's worked without issues for me.
It's essentially doing the same thing you're doing, but it's a lot
cleaner.
<sandbox:subForm id="cisActionManagerExecuteCisActionForm">
<h:selectOneMenu id="cisActionInput"
binding="#{cisActionManager.cisActionInputComponent}"
value="#{cisActionManager.cisAction}">
<f:selectItems
value="#{cisDAOSelectItemsProvider.cisActionItems}"/>
</h:selectOneMenu>
<h:inputText id="argumentInput"
binding="#{cisActionManager.argumentInputComponent}"
required="#{cisActionManager.argumentRequired}"
validator="#{cisActionManager.validateArgument}"
size="50"
value="#{cisActionManager.argument}">
<sandbox:submitOnEvent for="executeCisActionButton" />
</h:inputText>
</sandbox:subForm>
public boolean isArgumentRequired()
{
// NOTE: assumes that cisActionInput is already validated
UIInput cisActionInput = getCisActionInputComponent();
if (false == cisActionInput.isValid())
{
// Don't bother checking if cisActionInput is invalid
return false;
}
Object object = cisActionInput.getValue();
if (null == object)
{
// If there's no selection, then don't check the argument
return false;
}
if (false == object instanceof CisAction)
{
throw new FacesException("object is not of class
CisAction, but is class " + object.getClass().getName());
}
CisAction cisAction = (CisAction)object;
if (null == cisAction.getIsArgumentRequired())
{
throw new NullPointerException("isArgumentRequired is null
for cisAction " + cisAction);
}
return cisAction.getIsArgumentRequired().booleanValue();
}
I use the same process for validation:
public void validateArgument(FacesContext context, UIComponent
component, Object value)
{
if (false == value instanceof String)
{
throw new FacesException("argument value is not of class
String, but is class " + value.getClass().getName());
}
String argument = (String)value;
// NOTE: assumes that cisActionInput is already validated
UIInput cisActionInput = getCisActionInputComponent();
if (false == cisActionInput.isValid())
{
throw new FacesException("Should never reach here.
Checked in isArgumentRequired");
}
Object object = cisActionInput.getValue();
if (null == object)
{
// If there's no selection, then don't check the argument
throw new FacesException("Should never reach here.
Checked in isArgumentRequired");
}
if (false == object instanceof CisAction)
{
throw new FacesException("Should never reach here.
Checked in isArgumentRequired");
}
CisAction cisAction = (CisAction)object;
// perform validation here
}
On 3/2/07, Andrew Robinson <[EMAIL PROTECTED]> wrote:
Okay, I was too impatient to wait for a reply. I wouldn't mind to know
if anyone has a better approach, but I just got this to work by:
<label>
<t:selectBooleanCheckbox
id="myCheckbox"
value="#{bean.filter}" />
Search for value:
</label>
<t:inputText
required="#{bean.filter}"
value="#{bean.filterValue}">
<my:required compareTo="myCheckbox" value="#{true}" />
</t:inputText>
Where the required code is:
public class UIRequired
extends UIComponentBase
{
private final static String COMPONENT_FAMILY = "mypackage.Validation";
public final static String COMPONENT_TYPE = "mypackage.Required";
private String compareTo;
private Object value;
private String _for;
/**
* @return the value
*/
public Object getValue()
{
if (this.value != null) return this.value;
ValueBinding vb = getValueBinding("value");
return (vb == null) ? null : (Object)vb.getValue(getFacesContext());
}
/**
* @param value the value to set
*/
public void setValue(Object value)
{
this.value = value;
}
/**
* @return the for
*/
public String getFor()
{
if (this._for != null) return this._for;
ValueBinding vb = getValueBinding("_for");
return (vb == null) ? null : (String)vb.getValue(getFacesContext());
}
/**
* @param forVal the for to set
*/
public void setFor(String forVal)
{
this._for = forVal;
}
/**
* @return the compareTo
*/
public String getCompareTo()
{
if (this.compareTo != null) return this.compareTo;
ValueBinding vb = getValueBinding("compareTo");
return (vb == null) ? null : (String)vb.getValue(getFacesContext());
}
/**
* @param compareTo the compareTo to set
*/
public void setCompareTo(String compareTo)
{
this.compareTo = compareTo;
}
/**
* @see javax.faces.component.UIComponent#getFamily()
*/
@Override
public String getFamily()
{
return COMPONENT_FAMILY;
}
/**
* @see javax.faces.component.UIComponentBase#getRendererType()
*/
@Override
public String getRendererType()
{
return null;
}
/**
* @see
javax.faces.component.UIComponentBase#saveState(javax.faces.context.FacesContext)
*/
@Override
public Object saveState(FacesContext context)
{
return new Object[] {
super.saveState(context),
_for,
compareTo,
value,
};
}
/**
* @see
javax.faces.component.UIComponentBase#restoreState(javax.faces.context.FacesContext,
java.lang.Object)
*/
@Override
public void restoreState(FacesContext context, Object state)
{
Object[] arr = (Object[])state;
int index = -1;
super.restoreState(context, arr[++index]);
this._for = (String)arr[++index];
this.compareTo = (String)arr[++index];
this.value = arr[++index];
}
/**
* @see
javax.faces.component.UIComponentBase#processValidators(javax.faces.context.FacesContext)
*/
@Override
public void processValidators(FacesContext context)
{
super.processValidators(context);
String forVal = getFor();
UIInput input = null;
if (forVal != null)
input = (UIInput)this.findComponent(forVal);
if (input == null)
{
for (UIComponent comp = this; comp != null; comp = comp.getParent())
{
if (comp instanceof UIInput)
{
input = (UIInput)comp;
break;
}
}
}
UIInput compareToInput = (UIInput)this.findComponent(getCompareTo());
Object value = (compareToInput.isLocalValueSet()) ?
compareToInput.getLocalValue() : compareToInput.getValue();
input.setRequired(ObjectUtils.equals(value, getValue()));
}
}
BTW the "ObjectUtils.equals" method is just a handy method I use to
compare to values that may be null. If neither are null it uses the
equals method to compare.
On 3/2/07, Andrew Robinson <[EMAIL PROTECTED]> wrote:
> I have ideas on how to handle this situation, but none seem very
> elegant. What I have is a filter dialog for our application. The UI
> has options where the user chooses if they want to filter by
> something, and then they can type in the value. A simplistic
> representation:
>
> <label>
> <t:selectBooleanCheckbox
> value="#{bean.filter}" />
> Search for value:
> </label>
> <t:inputText
> required="#{bean.filter}"
> value="#{bean.filterValue}" />
>
> Now most sound notice the problem immediately, the required of the
> inputText will be evaluated against the bean's value which at
> validation time has not been updated yet, the value that I really
> want to compare is the submitted value of the checkbox.
>
> Marking the checkbox as immediate does not help, as that just changes
> when validation takes place in UIInput, not when the update is
> processed. There is no easy way I know of in JSF to have the required
> based on the state of the checkbox.
>
> Anyone know of a good solution for this without performing the
> validation in the INVOKE_APPLICATION phase (in the action or
> actionListener)?
>