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)?
>

Reply via email to