For JSF
novices, the immediate
attribute may seem a little confusing. Basically, is a pretty simple attribute,
if you know several stuff about it. For start, you have to know that the immediate
attribute can "accompany" both inputs (e.g. <h:inputText>) and
command (e.g. <h:commandButton>)
components. Depending on where it appears, it will affect the JSF lifecycle differently.
immediate in inputs (e.g. <h:inputText>)
Let's check
out the official documentation for immediate combined with inputs (e.g. <h:inputText>):
So, this
explanation is very straightforward, there is no need to repeat the same words
in different order. The prove of these words can be easily seen in the UIInput
source code of Mojarra. The Apply Request Values phase programmatically begins in
the processDecodes()
method. This is the moment when JSF loops the component tree, and for
each component, it invokes the corresponding decode() method. The request
values are extracted from request parameter map and are stored locally on
components via setSubmittedValue()
method. Let's see the code which reveals how immediate attribute is
treated:
// Mojarra
2.2.9 - javax.faces.component.UIInput
public void
processDecodes(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
if (!isRendered()) {
return;
}
super.processDecodes(context);
if (isImmediate()) {
executeValidate(context);
}
}
So, we are
in the Apply Request Values phase, and if the immediate attribute is true
(isImmediate()),
then the executeValidate()
method is called (this is happening after the decode() method was invoked
and the submitted value was stored). The executeValidate() method is
the validation process trigger (in "[OmniFaces utilities (2.0)]Get the value of the given UIInput whereby any unconverted submitted stringvalue will immediately be converted/validated", you saw that OmniFaces
invoke the validate()
method to force the validation to take place; well, the validate() method is
invoked by JSF from the executeValidate() method). "By
default", when the immediate attribute is false
(default value) the executeValidate() method is called from processValidations()
method, which mark the beginning of the Process Validations phase:
// Mojarra
2.2.9 - javax.faces.component.UIInput
public void
processValidators(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
if (!isRendered()) {
return;
}
pushComponentToEL(context, this);
if (!isImmediate()) {
Application application =
context.getApplication();
application.publishEvent(context,
PreValidateEvent.class, this);
executeValidate(context);
application.publishEvent(context,
PostValidateEvent.class, this);
}
for (Iterator<UIComponent> i =
getFacetsAndChildren(); i.hasNext(); ) {
i.next().processValidators(context);
}
popComponentFromEL(context);
}
Notice the verification
(!isImmediate())
and the call of executeValidate()
method - this verification is needed for preventing, as soon as possible, the
re-validation of inputs in the Process Validations phase. Basically, everything
can be resumed to the moment in time when the executeValidate() method is
invoked.
The below
diagram reveals how the JSF lifecycle is altered by the immediate="true"
in inputs:
We can
"play" with the OmniFaces utilities method, hasSubmittedValue() to see
what's happening with the submitted value depending on the value of the immediate
attribute. In "[OmniFaces utilities (2.0)] Get whether the given editable value holder component has a submitted value"
you saw that the submitted value has a short lifespan between Apply Request
Values and Process Validations phase.
So, when the
immediate="true"
the OmniFaces utilities, hasSubmittedValue()
returns always false
in a before/after phase check. This is normally, because the submitted value is
available only inside the Apply Request Values phase and for a short time - after
the decode()
method is invoked, and until the validate() method ends its execution. When
the Process Validations begins, the submitted value is null.
immediate in commands (e.g. <h:commandButton>)
Again, let's
check out the official documentation for immediate combined with commands (e.g.
<h:commandButton>):
// Mojarra
2.2.9 - javax.faces.component.UICommand
public void
queueEvent(FacesEvent e) {
UIComponent c = e.getComponent();
if (e instanceof ActionEvent && c
instanceof ActionSource) {
if (((ActionSource) c).isImmediate()) {
e.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else {
e.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
super.queueEvent(e);
}
Basically,
the immediate="true"
will "accompany" commands of type Cancel buttons (which resets
the form's fields to the initial state or to the most recent valid state) and Clear
(which clears up the form's fields). The idea is pretty simple: the user wants
to cancel/clear its inputs, and it press a button labeled Cancel/Clear.
Obviously, we don’t want to require to the user to provide some valid values
before cancelling/clearing, but this is exactly what we will do after the
button is pressed. The current form is submitted and the validation process
will (most probably) fail and the managed bean method designated to cancel
(reset) the values will not be invoked. This is happening because the Process
Validations phase take place before Invoke Application phase. Per example, if
an <h:inputText>
value will not successfully pass through validation, then JSF will
"jump" directly to Render Response phase, so the Invoke Application
phase is skipped:
// Mojarra
2.2.9 - javax.faces.component.UIInput
private void
executeValidate(FacesContext context) {
try {
validate(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.validationFailed();
context.renderResponse();
}
}
But, when
the immediate="true"
is added to the Cancel
button the above scenario will be re-written. This attribute will instruct JSF
to invoke the managed bean method responsible with canceling right from the
Apply Request Values phase. Afterwards, go directly in Response Render phase.
So, no validations take place!
Let's
consider this - let the input textbox (name) empty and press the Cancel
button:
<h:form
id="myForm">
Name: <h:inputText id="nameId"
value="#{playersBean.name}" required="true"
validator="myValidator"/>
<h:commandButton value="Save"
action="#{playersBean.save()}"/>
<h:commandButton value="Cancel"
action="#{playersBean.cancel()}"/>
</h:form>
In this
case, when the Cancel
button is pressed you will see the validation error messages, because the
submitted value will pass through validation. Now, add the
immediate="true" to the Cancel button:
<h:form
id="myForm">
Name: <h:inputText id="nameId"
value="#{playersBean.name}" required="true"
validator="myValidator"/>
<h:commandButton value="Save"
action="#{playersBean.save()}"/>
<h:commandButton immediate="true" value="Cancel"
action="#{playersBean.cancel()}"/>
</h:form>
Now, when
the Cancel
button is pressed, the submitted value will not be validated and the cancel()
method will be called from Apply Request Values phase.
The cancel()
method will be something like this:
public
String cancel() {
// reset the form's fields to the initial
state or to the most recent valid state - practically do nothing here
return "index";
}
The below
diagram reveals how the JSF lifecycle is altered by the immediate="true"
for the above case:
We can
"play" with the OmniFaces utilities method, hasSubmittedValue() to see
what's happening with the submitted value depending on the value of the immediate
attribute. In "[OmniFaces utilities (2.0)] Get whether the given editable value holder component has a submitted value"
you saw that the submitted value has a short lifespan between Apply Request
Values and Process Validations phase.
The immediate
attribute is available for AJAX requests as well, but AJAX provides a better
solution for these kinds of tasks. Instead of using immediate="true",
we can use the @this
keyword. Furthermore, we can use the resetValues feature to simplify and
fortify the Cancel/Clear
buttons. Or, if you are not a fan of resetValues, you can put render="@form":
...
<h:commandButton
value="Cancel">
<f:ajax execute="@this"
render="@form"/>
</h:commandButton>
...
Done! You
have to read more on StackOverflow!
Niciun comentariu :
Trimiteți un comentariu