v.3.1.0-SNAPSHOT SVN $Revision: 2041 $ $Date: 2007-08-02 18:28:53 -0700 (Thu, 02 Aug 2007) $
Example of INCORRECT approach to the problem
Enter credit card number:
Or use your pre-defined credit card: Visa
Your entered credit card number:
Example of CORRECT approach to the problem
Enter credit card number:
Or use your pre-defined credit card: Visa
Your entered credit card number:
Example of ALTERNATIVE approach to the problem
Enter credit card number:
Or use your pre-defined credit card: Visa
Your entered credit card number:

Use case

I have an input field where I can enter Credit Card Number. At the same time I have some pre-defined CC in a system, which I can choose. When I click on command link with id of pre-defined CC, it value propagated to input.

Simple solution

So my first solution is simple. The idea is to have an action behind command link and assign pre-defined value to CC Number.

Page Sources

<h:outputText value="Enter credit card number:"/>
<h:inputText value="#{data.creditCard}"/>
<h:outputText value="Or use your pre-defined credit card:"/>
<a4j:commandLink immediate="true" action="#{data.useMyCreditCard}" reRender="..." value="#{data.myCreditCardName}"/>
<h:outputText value="Your entered credit card number:"/>
<h:outputText value="#{data.creditCard}" styleClass="output"/>

Bean Source (getters/setters removed)

public String useMyCreditCard() {
setCreditCard(getMyCreadtCard());
return null;
}

Problem

However, soon I discover that my simple solution does not works. The reason is simple: inputText is a little bit more complicated than you think. In addition to value binding it has two more values inside: submittedValue and localValue. Both are used during a request processing. The goal is simple – whenever feasible inputText display value that was entered by user, not value from model. The most obvious case – when validation error occurs. The general rule is – submitted value is what come from http request. After conversion and validation submittedValue is cleared and localValue assigned. After update model localValue is cleared also. During rendering component looks for submittedValue first, if any exists, than it used for rendering. If no submittedValue present – that localValue will be considered. If no localValue exists – than value binding will be evaluated to obtain value from model.

So, valid solution is simple – you need to reset submitted and local value in component. There are many different ways to do so. In my example I'm using the most straightforward approach – use component binding and API to reset values.

Valid Page Sources

<h:outputText binding="#{data.component}" value="Enter credit card number:"/>
<h:inputText value="#{data.creditCard}"/>
<h:outputText value="Or use your pre-defined credit card:"/>
<a4j:commandLink immediate="true" action="#{data.useMyCreditCard}" reRender="..." value="#{data.myCreditCardName}"/>
<h:outputText value="Your entered credit card number:"/>
<h:outputText value="#{data.creditCard}" styleClass="output"/>

Valid Bean Source (getters/setters removed)

public String useMyCreditCard() {
setCreditCard(getMyCreadtCard());
getComponent().setSubmittedValue(null);
getComponent().setValue(null); //This is actually the local value!
return null;
}

Alternative approach

Alternative approach uses a4j:region component to limit set of processed inputs. As the result submittedValue is not pushed to the input component (because it is outside of the a4j:region). Therefore, component will evaluate value binding and use value from the model for rendering.

Alternative Page Sources

<h:outputText value="Enter credit card number:"/>
<h:inputText value="#{data.creditCard}"/>
<h:outputText value="Or use your pre-defined credit card:"/>
<a4j:region>
<a4j:commandLink immediate="true" action="#{data.useMyCreditCard}" reRender="..." value="#{data.myCreditCardName}"/>
</a4j:region>
<h:outputText value="Your entered credit card number:"/>
<h:outputText value="#{data.creditCard}" styleClass="output"/>

Alternative Bean Source (getters/setters removed)

public String useMyCreditCard() {
setCreditCard(getMyCreadtCard());
return null;
}

Success

Hopefully now my solution works!

Conclusion

You need to be careful with input components. In some cases you have to be aware of their complex internal structures.

My example is based on JSF 1.2 RI and Facelets. You can download it from here. Here is page source. Here is bean source.