My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

luni, 23 februarie 2015

JSF 2.2 - immediate attribute demystified

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>):
These words are coded in UICommand of Mojarra, as below:

// 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

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors