Thursday, October 22, 2009

Immediate Components



Immediate Components


In "Life
Cycle Events" on page 268, we saw that value change events are normally fired
after the Process Validations phase, and action events are normally fired after
the Invoke Application phase. Typically, that is the preferred behavior. You
usually want to be notified of value changes only when they are valid, and
actions should be invoked after all submitted values have been transmitted to
the model.


But sometimes you want value change
events or action events to fire at the beginning of the life cycle to bypass
validation for one or more components. In "Using Immediate Input Components"
on page 288 and
"Bypassing
Validation" on page 237, we make compelling arguments for such
behavior. For now, we will look at the mechanics of how immediate events are
delivered, as illustrated by Figure 7-6.





Figure 7-6. Immediate components


[View full size image]





Immediate events are fired after the
Apply Request Values phase. For input components, conversion and validation are
performed after the Apply Request Values phase, and value change events are
subsequently fired. For command components, action listeners are invoked,
followed by actions; that process kicks in the navigation handler and
circumvents the rest of the life cycle up to Render Response.


Using Immediate Input
Components


Figure 7-7 shows the value change
example discussed in "Value
Change Events" on page 269. Recall that the application uses a value change listener
to change the view's locale, which in turn changes the localized state prompt
according to the selected locale.





Figure 7-7. Unwanted validation






Here we have made a seemingly innocuous
change to that application: We added a required validator to the
Address field and added a message tag to the
form. But that validation results in an error when we
select a country
without filling in the Address field (recall that the country menu submits its form when
its value is changed).


The problem is this: We want
validation to kick in when the submit button is activated, but not when the
country is changed. How can we specify validation for one but not the other?


The solution is to make the country menu
an immediate
component. Immediate input components perform conversion and validation, and
subsequently deliver value change events at the beginning of the JSF life
cycle—after the Apply Request Values—instead of after Process Validations.


We specify immediate
components with the immediate attribute, which is
available to all input and command components:



   <h:selectOneMenu value="#{form.country}" onchange="submit()"immediate="true"
valueChangeListener="#{form.countryChanged}">
<f:selectItems value="#{form.countryNames}"/>
</h:selectOneMenu>



With the immediate attribute set to true, our menu fires value change events after Apply Request
Values, well before any other input components are validated. You may wonder
what good that does us if the other validations happen later instead of
sooner—after all, the validations will still be performed and the validation
error will still be displayed. To prevent validations for the other components
in the form, we have one more thing to do, which is to call the faces context
renderResponse method at the end of our
value change listener, like this:


  private static final String US = "United States";
...
public void countryChanged(ValueChangeEvent event) {
FacesContext context = FacesContext.getCurrentInstance();

if (US.equals((String) event.getNewValue()))
context.getViewRoot().setLocale(Locale.US);
else
context.getViewRoot().setLocale(Locale.CANADA);

context.renderResponse();
}


The call to renderResponse()
skips the rest of the life cycle—including validation of the rest of the input
components in the form—up to Render Response. Thus, the other validations are
skipped and the response is rendered normally (in this case, the current page is
redisplayed).


To summarize, you can skip validation
when a value change event fires by doing the following:






  1. Adding an immediate attribute to your input tag




  2. Calling FacesContext.renderResponse() at the end of your listener


One more thing is noteworthy about this
example. Notice that we add an onchange attribute whose
value is submit() to our h:selectOneMenu tag. Setting that
attribute means that the JavaScript submit
function will be invoked whenever someone changes the selected value of the
menu, which causes the surrounding form to be submitted.


That form submit is crucial because the JSF implementation handles all events on the
server
. If you take out the onchange attribute, the form will not be submitted when the selected
menu item is changed, meaning that the JSF life cycle will never be invoked, our
value change listener will never be called, and the locale will never be
changed.


You may find it odd
that JSF handles all events on the server, but remember that you can handle events on the
client if you wish by attaching JavaScript to components with attributes such as
onblur, onfocus, onclick, etc.
Also, client-side event handling is on the table for the next version of
JSF.


Using Immediate Command
Components


In Chapter
4 we discussed an application, shown in Figure 7-8, that uses command links to change locales.





Figure 7-8. Changing locales with links






If we add a required validator to
one of the input fields in the form, we will have the same problem we had with
the application discussed in "Using Immediate Input Components"
on page 288: The validation error will appear when we just want to
change the locale by clicking a link. This time, however, we need an immediate
command component instead
of an immediate input component. All we need to
do is add an immediate attribute to our h:commandLink tag,
like this:


  <h:commandLink action="#{localeChanger.germanAction}" immediate="true">
<h:graphicImage value="/german_flag.gif" style="border: 0px"/>
</h:commandLink>


Unlike value change events, we do not
need to modify our listener to invoke FacesContext.renderResponse() because all actions, immediate or not, proceed directly
to the Render Response phase, regardless of when they are fired.

No comments:

Post a Comment