Search code examples
jsfcomponentslifecycle

When are setValue and setSubmittedValue called on UIComponent?


If I correctly combined the information contained in BalusC's great 2006 post http://balusc.blogspot.ch/2006/09/debug-jsf-lifecycle.html with Optimus Prime's even earlier post http://cagataycivici.wordpress.com/2005/12/28/jsf_component_s_value_local/ I get the following:

My understanding:

  1. During the APPLY_REQUEST_VALUES phase,
    • the input value is set to a submittedValue property of the UI component (e.g. inputComponent.setSubmittedValue("test")).
  2. During the PROCESS_VALIDATIONS phase,
    • the same values are read from the submittedValue property (presumably inputComponent.getSubmittedValue()) and used for conversion, if necessary.
    • If the conversion was successful or skipped, the result is set to a value property of the component (e.g. inputComponent.setValue("test")).
    • Also, the submittedValue is erased again immediately (e.g. inputComponent.setSubmittedValue(null))
    • the (converted) value is read from the value property of the UI component (presumably inputComponent.getValue()) and validated.
    • after validation, the backing bean/model's stored value is read (e.g. myBean.getInputValue()) and compared with the newly converted and validated value. If different, the valueChangeListener method(s) will be called.
  3. During the UPDATE_MODEL_VALUES phase,
    • the newly converted and validated value is finally stored in the backing bean's property field (e.g. myBean.setInputValue("test")).

Questions:

  • Is this correct?
  • Is there something missing for a full understanding of what goes on between the POST and the saving of the input value in the backing bean?
  • With immediate="true" on the Input Component, are we merely shifting these events to the APPLY_REQUEST_VALUES phase or do we change more than just the timing/order of events?

Solution

  • Almost correct. The component's local value is only set when conversion and validation is successful. After that, the submitted value is set to null. You can find the entire process of the validations phase in a rather self-documenting way in the UIInput#validate() method (line numbers are conform JSF 2.1 API):

    934    public void validate(FacesContext context) {
    935 
    936         if (context == null) {
    937             throw new NullPointerException();
    938         }
    939 
    940         // Submitted value == null means "the component was not submitted
    941         // at all".  
    942         Object submittedValue = getSubmittedValue();
    943         if (submittedValue == null) {
    944             return;
    945         }
    946 
    947         // If non-null, an instanceof String, and we're configured to treat
    948         // zero-length Strings as null:
    949         //   call setSubmittedValue(null)
    950         if ((considerEmptyStringNull(context)
    951              && submittedValue instanceof String 
    952              && ((String) submittedValue).length() == 0)) {
    953             setSubmittedValue(null);
    954             submittedValue = null;
    955         }
    956 
    957         Object newValue = null;
    958 
    959         try {
    960             newValue = getConvertedValue(context, submittedValue);
    961         }
    962         catch (ConverterException ce) {
    963             addConversionErrorMessage(context, ce);
    964             setValid(false);
    965         }
    966 
    967         validateValue(context, newValue);
    968 
    969         // If our value is valid, store the new value, erase the
    970         // "submitted" value, and emit a ValueChangeEvent if appropriate
    971         if (isValid()) {
    972             Object previous = getValue();
    973             setValue(newValue);
    974             setSubmittedValue(null);
    975             if (compareValues(previous, newValue)) {
    976                 queueEvent(new ValueChangeEvent(this, previous, newValue));
    977             }
    978         }
    979 
    980     }
    

    As to the immediate attribute on the UIInput component, yes this merely shifts the validation to the apply request values phase. See also the source code of UIInput#processDecodes() and UIInput#processValidators(), there's a check on UIInput#isImmediate().