Add immediate="true" to UIInput and UICommand
Extend the h:inputText as well as the h:commandButton in the test.jsp with immediate="true" :
... <h:inputText binding="#{myBean.inputComponent}" value="#{myBean.inputValue}" valueChangeListener="#{myBean.inputChanged}" immediate="true"> ... <h:commandButton value="submit" action="#{myBean.action}" immediate="true" /> ...
The form submit with the value "test" entered should output at least:
START PHASE RESTORE_VIEW 1
MyBean <init>: constructed
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
END PHASE RESTORE_VIEW 1
START PHASE APPLY_REQUEST_VALUES 2
MyConverter getAsObject: test
MyValidator validate: test
MyBean getInputValue: null
MyBean inputChanged: null to test
MyBean action: succes
END PHASE APPLY_REQUEST_VALUES 2
START PHASE RENDER_RESPONSE 6
MyConverter getAsString: test
MyBean getOutputValue: null
END PHASE RENDER_RESPONSE 6
1. Restore view.
The bean is constructed. The UIViewRoot is restored from session and the bounded components are set in the component bindings.
2. Apply request values.
Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot , for example inputComponent.setSubmittedValue("test" ) . The submitted values are immediately passed through the converter getAsObject() method and validated by the validator. If the conversion and validation succeeds, then the initial input value will be retrieved from the value binding getter and behind the scenes the inputComponent.setValue(submittedValue) and inputComponent.setSubmittedValue(null ) will be executed. If the retrieved initial input value differs from the submitted value, then the valueChangeListener method will be invoked. This all happens in this phase instead of the Process validations phase due to the immediate="true" in the h:inputText . Finally the real processing of the form submission happens here. This happens in this phase instead of the Invoke application phase due to the immediate="true" in the h:commandButton .
验证,应用都在这个阶段执行!
3. Process validations.
This phase is skipped due to the immediate="true" in the h:commandButton .
4. Update model values.
This phase is skipped due to the immediate="true" in the h:commandButton .
5. Invoke application.
This phase is skipped due to the immediate="true" in the h:commandButton .
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean. If a converter is definied, then the value will be passed through the converter getAsString() method and the result will be shown in the form.
Note for all components with immediate : as the Update model values phase is skipped, the value bindings aren't been set and the value binding getters will return null. But the values are still available by the relevant components in the UIViewRoot . In this case you can retrieve the input value from inputComponent.getValue() in the action() method. The new input value is also available by the ValueChangeEvent in the inputChanged() method. You could even change it using inputComponent.setValue(newValue) in the action() method.
在action()中可以发现,这种情况下,inputComponent.getValue() 可以得到值,而getSubmittedValue已经被reset null.
Note for other components without immediate : any other UIInput components inside the same form which don't have immediate="true" set will not be converted, validated nor updated, but behind the scenes the inputComponent.setSubmittedValue(submittedValue) will be executed before the action() method will be executed. You can retrieve the non-converted and non-validated input value from inputComponent.getSubmittedValue() in the action() method. You could even change it using inputComponent.setSubmittedValue(newValue) in the action() method .
Conversion error
Let's see what happens if a conversion error will occur. Change the getAsObject() method of MyConverter.java as follows (and remove the immediate="true" from the test.jsp file):
package mypackage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
public class MyConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String value) {
System.out.println("MyConverter getAsObject: " + value);
throw new ConverterException("Conversion failed.");
}
...
}
The form submit with the value "test" entered should output at least:
START PHASE RESTORE_VIEW 1
MyBean <init>: constructed
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
END PHASE RESTORE_VIEW 1
START PHASE APPLY_REQUEST_VALUES 2
END PHASE APPLY_REQUEST_VALUES 2
START PHASE PROCESS_VALIDATIONS 3
MyConverter getAsObject: test
END PHASE PROCESS_VALIDATIONS 3
START PHASE RENDER_RESPONSE 6
MyBean getOutputValue: null
END PHASE RENDER_RESPONSE 6
1. Restore view.
The bean is constructed. The UIViewRoot is restored from session and the bounded components are set in the component bindings.
2. Apply request values.
Nothing to see here. Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot , for example inputComponent.setSubmittedValue("test" ) .
3. Process validations.
The submitted values are passed through the converter getAsObject() method, where a ConverterException is thrown. The validator and the valueChangeListener are bypassed. Also the inputComponent.setValue(submittedValue) won't take place. The lifecycle will proceed to the Render response phase immediately.
Convert 异常发生,跳过验证,更新,应用,直接到最后的阶段!
4. Update model values.
This phase is skipped due to the ConverterException.
5. Invoke application.
This phase is skipped due to the ConverterException.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean, expecting the values for which a ConverterException has occurred. Behind the scenes those will be retrieved from the components in the UIViewRoot , e.g. inputComponent.getSubmittedValue() .
发生异常的,会从inputComponent.getSubmittedValue()取值!
Note : any other UIInput components inside the same form which don't throw a ConverterException will continue the lifecycle as usual , only the invoke application phase will still be skipped.
Validation error
Let's see what happens if a validation error will occur. Change the validate() method of MyValidator.java as follows (and remove the immediate="true" from the test.jsp file and revert MyConverter.java back to original):
package mypackage;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
public class MyValidator implements Validator {
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
System.out.println("MyValidator validate: " + value);
throw new ValidatorException(new FacesMessage("Validation failed."));
}
}
The form submit with the value "test" entered should output at least:
START PHASE RESTORE_VIEW 1
MyBean <init>: constructed
MyBean setInputComponent: javax.faces.component.html.HtmlInputText@2a9fca57
MyBean setOutputComponent: javax.faces.component.html.HtmlOutputText@13bbca56
END PHASE RESTORE_VIEW 1
START PHASE APPLY_REQUEST_VALUES 2
END PHASE APPLY_REQUEST_VALUES 2
START PHASE PROCESS_VALIDATIONS 3
MyConverter getAsObject: test
MyValidator validate: test
END PHASE PROCESS_VALIDATIONS 3
START PHASE RENDER_RESPONSE 6
MyBean getOutputValue: null
END PHASE RENDER_RESPONSE 6
1. Restore view.
The bean is constructed. The UIViewRoot is restored from session and the bounded components are set in the component bindings.
2. Apply request values.
Nothing to see here. Behind the scenes the submitted form values are obtained as request parameters and set in the relevant components in the UIViewRoot , for example inputComponent.setSubmittedValue("test" ) .
3. Process validations.
The values are retrieved as objects from the components, passed through the converter getAsObject() method and validated by the validator, where a ValidatorException is thrown. The valueChangeListener is bypassed. Also the inputComponent.setValue(submittedValue) won't take place. The lifecycle will proceed to the Render response phase immediately.
4. Update model values.
This phase is skipped due to the ValidatorException.
5. Invoke application.
This phase is skipped due to the ValidatorException.
6. Render response.
The values to be shown are retrieved from the value binding getters in the backing bean, expect of the values for which a ValidatorException has occurred. Behind the scenes those will be retrieved from the components in the UIViewRoot , e.g. inputComponent.getSubmittedValue() .
Note : any other UIInput components inside the same form which don't throw a ValidatorException will continue the lifecycle as usual , only the invoke application phase will still be skipped.
Okay, when should I use the immediate attribute?
If it isn't entirely clear yet, here's a summary, complete with real world use examples when they may be beneficial:
- If set in UIInput (s) only, the process validations phase will be taken place in apply request values phase instead. Use this to prioritize validation for the UIInput component(s) in question. When validation/conversion fails for any of them, the non-immediate components won't be validated/converted.
当组件设置“即时",那么验证就会在第二阶段是,可以优先让它得到验证,避免其他非即时组件的验证与转化!
- If set in UICommand only, the apply request values phase until with update model values phases will be skipped for any of the UIInput component(s). Use this to skip the entire processing of the form. E.g. "Cancel" or "Back" button.
如果是按钮设置“即时”,那么可以跳过验证与转化的过程!
- If set in both UIInput and UICommand components, the apply request values phase until with update model values phases will be skipped for any of the UIInput component(s) which does not have this attribute set. Use this to skip the processing of the entire form expect for certain fields (with immediate). E.g. "Password forgotten" button in a login form with a required and immediate username field and a required but non-immediate password field.
如果提交按钮与组件都设置“即时",那么可以跳过非即时 组件的验证与转化!