[OmniFaces utilities] The
getValue()
method returns the value of the given editable value holder component without the need to know if the given component has already been converted/validated or not. Note that it thus returns the unconverted submitted string value when the conversion/validation hasn't been taken place for the given component and it returns the converted object value -if applicable- when conversion/validation has been taken place for the given component.
In this
post, we will use an OmniFaces utilities method from Components class to make a quick
analyze of what is happening with a piece of information (value) provided by
the user via an EditableValueHolder
(e.g. UIInput)
during JSF phases.
This method
is named, getValue()
and you can see its source in the below figure:
So,
basically this method returns the result obtained via getSubmittedValue() or getLocalValue(). The
OmniFaces documentation says: "Returns
the value of the given editable value holder component without the need to know
if the given component has already been converted / validated or not. Note that
it thus returns the unconverted submitted string value when the conversion /
validation hasn't been taken place for the given component and it returns the
converted object value -if applicable- when conversion/validation has been
taken place for the given component."
Well, for a
novice, this explanation may sound pretty confusing. If you are not very
familiar with the differences between submitted
value and local value, or you
worked with the user provided inputs only via getters and setters then this
post may help you to clear up these things. So, let's see an example that will
make us believe that this method is pretty dummy. In a JSF page you may have
this - just a simple EditableValueHolder
(UIInput)
we an attached custom converter and validator:
<h:form
id="myForm">
Player Name:
<h:inputText id="nameId"
value="#{playersBean.name}" converter="myConverter"
validator="myValidator"/>
<h:commandButton id="maxBtnId" value="Save"
action="#{playersBean.save()}"/>
</h:form>
The custom
converter and validator indicated above were "patched" with some SOPs
and will help us to see how the converter and validator is called in
conjunction with JSF phases:
·
custom converter (converts between lower and
upper case)
@FacesConverter(value =
"myConverter")
public class MyConverter implements
Converter {
@Override
public Object getAsObject(FacesContext fc,
UIComponent uic, String string) {
System.out.println("MY CONVERTER: getAsObject()");
return string.toUpperCase();
}
@Override
public String getAsString(FacesContext fc,
UIComponent uic, Object o) {
System.out.println("MY CONVERTER:
getAsString()");
return o.toString().toLowerCase();
}
}
·
custom validator (this is a dummy validator)
@FacesValidator(value =
"myValidator")
public class MyValidator implements
Validator {
@Override
public void validate(FacesContext fc,
UIComponent uic, Object o) throws ValidatorException {
System.out.println("MY VALIDATOR: validate()");
}
}
Finally, the
managed bean relevant code is:
import
org.omnifaces.util.Components;
import org.omnifaces.util.Faces;
...
@Named
@SessionScoped
public class PlayersBean implements
Serializable {
private String name;
public void save(){
// some
saving stuff
EditableValueHolder uiInputName =
(EditableValueHolder)Faces.getViewRoot().findComponent("myForm:nameId");
String
uiInputNameValue = Components.getValue(uiInputName);
System.out.println("VALUE RETURNED BY OmniFaces#getValue():
" + uiInputNameValue);
System.out.println("VALUE RETURNED BY
GETTER: " + getName());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If you check
the highlighted code you can see how the Components.getValue() method was
called for the EditableValueHolder
represented by our UIInput
component. Now, let's say that we typed in the text box the name rafael nadal, and we clicked
the Save
button. The big question now is: after we submit the form (at postback), what
will display the SOPs from our custom converter/validator and the two SOPs from
the save()
method above? Well, here it is the output:
MY CONVERTER: getAsObject()
MY VALIDATOR: validate()
VALUE RETURNED BY OmniFaces#getValue(): null
VALUE RETURNED BY GETTER: RAFAEL NADAL
MY CONVERTER: getAsString()
Well, the
OmniFaces getValue()
returned null,
while the getter method returned exactly what we have expected (the rafael nadal text in
uppercase)! Repeating the scenario will reveal the same result every time! So,
what is going on? Is the OmniFaces getValue() method useless ?! Obviously, not! The issue
consist in the fact that we did not correctly understood what the method does,
and we have implemented a wrong scenario. If you have expected to see the same
result returned by getValue()
and the getter method, then you have to read further.
With a
little work, we can easily write e simple phase listener that will help us to
debug the JSF phases. The idea is to call the OmniFaces getValue() method before and after
each JSF phase, and reveal the results via some simple SOPs:
public class DebugPhaseListener
implements PhaseListener {
public DebugPhaseListener() {
}
@Override
public void afterPhase(PhaseEvent event) {
System.out.println("AFTER PHASE # " + event.getPhaseId());
UIViewRoot root = Faces.getViewRoot();
if (root != null) {
UIComponent comp =
root.findComponent("myForm:nameId");
if (comp != null) {
Object value = Components.getValue((EditableValueHolder) comp);
System.out.println("VALUE #
" + value);
}
} else {
System.out.println("NO VIEW ROOT
AVAILABLE !");
}
}
@Override
public void beforePhase(PhaseEvent event) {
System.out.println("BEFORE PHASE # " + event.getPhaseId());
UIViewRoot root = Faces.getViewRoot();
if (root != null) {
UIComponent comp =
root.findComponent("myForm:nameId");
if (comp != null) {
Object value = Components.getValue((EditableValueHolder) comp);
System.out.println("VALUE #
" + value);
}
} else {
System.out.println("NO VIEW ROOT
AVAILABLE !");
}
}
@Override
public PhaseId getPhaseId() {
return
PhaseId.ANY_PHASE;
}
}
In addition,
we have " patched" with a SOP the OmniFaces getValue() method itself:
public static <T> T
getValue(EditableValueHolder component) {
Object submittedValue =
component.getSubmittedValue();
System.out.println("OmniFaces
Components.getValue(): SUBMITTED VALUE: " + submittedValue + " LOCAL VALUE:
"+component.getLocalValue());
return (T) ((submittedValue != null) ?
submittedValue : component.getLocalValue());
}
Is time to
see what is really happening behind the scene. We repeated the scenario, and
the outputs are discussed below:
·
check RESTORE_VIEW
phase
Before this
phase, there is no view root (is null), so the getValue() is not called. Right after this point, the
component tree will be build:
BEFORE PHASE # RESTORE_VIEW 1
NO VIEW ROOT AVAILABLE !
After this
phase, the component tree was restored (built), but the request parameter (request value) have
not been extracted yet, so, at this point, the submitted value and the local
value are both null:
AFTER PHASE # RESTORE_VIEW 1
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL VALUE: null
VALUE # null
·
check APPLY_REQUEST_VALUES
phase
Well, we are
just after the RESTORE_VIEW
and before the APPLY_REQUEST_VALUES,
so we have the same output:
BEFORE PHASE # APPLY_REQUEST_VALUES
2
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL VALUE: null
VALUE # null
In this
phase, the decode()
method is called and the request parameter is stored locally via setSubmittedValue() method (this
value is known as submitted value). This means that the stored value is exactly
the value provided by the user. It was not converted or validated. After this
phase, you can notice that the getValue() returns the submitted value (e.g. rafael nadal text):
AFTER PHASE # APPLY_REQUEST_VALUES
2
OmniFaces Components.getValue():
SUBMITTED VALUE: rafael nadal LOCAL VALUE: null
VALUE # rafael nadal
·
check PROCESS_VALIDATIONS
phase
Well, we are
just after the APPLY_REQUEST_VALUES
and before the PROCESS_VALIDATIONS,
so we have the same output:
BEFORE PHASE # PROCESS_VALIDATIONS
3
OmniFaces Components.getValue():
SUBMITTED VALUE: rafael nadal LOCAL
VALUE: null
VALUE # rafael nadal
In this
phase, the converters and validators are called. The converted and validated
value is further stored as local value via setValue() and setLocalValueSet() flag
method (a submitted value that was converted and validated, but not stored yet
as local value is known as new value). Moreover, the earlier submitted
value is reset by calling setSubmittedValue(null). Notice that, after this phase, the
OmniFaces getValue()
reveals the local value, which was converted, validated and stored, and the
submitted value, which was available after APPLY_REQUEST_VALUES phase, but is null now:
MY CONVERTER: getAsObject()
MY VALIDATOR: validate()
AFTER PHASE # PROCESS_VALIDATIONS 3
OmniFaces Components.getValue(): SUBMITTED
VALUE: null LOCAL VALUE: RAFAEL NADAL
VALUE # RAFAEL NADAL
·
check UPDATE_MODEL_VALUES
phase
Well, we are
just after the PROCESS_VALIDATIONS
and before the UPDATE_MODEL_VALUES,
so we have the same output:
BEFORE PHASE # UPDATE_MODEL_VALUES
4
OmniFaces Components.getValue(): SUBMITTED
VALUE: null LOCAL VALUE: RAFAEL NADAL
VALUE # RAFAEL NADAL
In this
phase, the data model value is updated from the local value and the local value
is reset to null.
Actually, at this point Mojarra calls the resetValue() method, which will
reset the submitted value (which is null anyway), the local value and the flag indicating that
the local value was set. So, after this phase, the OmniFaces getValue() will return
always null
(both, submitted and local value were reset):
AFTER PHASE # UPDATE_MODEL_VALUES 4
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL VALUE: null
VALUE # null
·
check INVOKE_APPLICATION
phase
Well, during
this phase, JSF will invoke the PlayersBean.save() method. Remember that, from this method,
we have called the OmniFaces getValue() method. Now, we know why the returned result was
invariably null,
and why the getter returns the expected value:
BEFORE PHASE # INVOKE_APPLICATION 5
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL
VALUE: null
VALUE # null
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL
VALUE: null
VALUE RETURNED BY
OmniFaces#getValue(): null
VALUE RETURNED BY GETTER: RAFAEL
NADAL
AFTER PHASE # INVOKE_APPLICATION 5
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL
VALUE: null
VALUE # null
·
check RENDER_RESPONSE
phase
Well, we are
just after the INVOKE_APPLICATION
and before the RENDER_RESPONSE,
so we have the same output. During this phase the converter getAsString() method is
called:
BEFORE PHASE # RENDER_RESPONSE 6
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL
VALUE: null
VALUE # null
MY CONVERTER: getAsString()
After this
phase, we have the same output:
AFTER PHASE # RENDER_RESPONSE 6
OmniFaces Components.getValue():
SUBMITTED VALUE: null LOCAL
VALUE: null
VALUE # null
The
request-response cycle is over (the postback passed through all JSF phases). Now
you know what the OmniFaces getValue() method returns depending on the current JSF
phase, and you know how to correctly use it in your projects. Moreover, you
know what is happening with a piece of information provided by the user from the
point of submission to the response:
For sake of completeness,
you may be interesting in reading "JSFConverters and Validators Demystified"
Niciun comentariu :
Trimiteți un comentariu