There is very common myth among ADF and JSF developers that using Immediate attribute will avoid unnecessary validation in any case. It is only partly true. The myth is growing up because of misunderstanding how the Immediate attribute actually works. Using the attribute without understanding of the mechanism very often lead to bugs and unpredictable behaviour of the application. I hope, this post will be useful for developers to understand and to remember when the Immediate attribute is a good option and when it is not.
I think, we should be aware of the following key points:
In order to understand how these points actually affect the system's behaviour, I'm going to have some experiments with very simple form:
And the jspx for the form:
I've added log messages to the valueChangeListeners, setters, and actionListener in order to follow the request process:
So, Experiment #1. Standard situation, nobody has Immediate attribute:
Input values and click the button:
In the system log we can see the following:
firstInputListener {
currentPhase = PROCESS_VALIDATIONS 3
NewValue = 1
}
secondInputListener {
currentPhase = PROCESS_VALIDATIONS 3
NewValue = 2
}
firstInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
secondInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
actionEventListener {
currentPhase = INVOKE_APPLICATION 5
firstComponentValue = 1
secondComponentValue = 2
firstModelValue = 1
secondComponentValue = 2
}
Everything is good. ValueChangelisteners are delivered to the Process Validation phase, setters are fired at the Update Model phase and actionListener is delivered to the Invoke Application phase.
Experiment #2. FirstInput has Immediate attribute:
And the log:
firstInputListener {
currentPhase = APPLY_REQUEST_VALUES 2
NewValue = 1
}
secondInputListener {
currentPhase = PROCESS_VALIDATIONS 3
NewValue = 2
}
firstInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
secondInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
actionEventListener {
currentPhase = INVOKE_APPLICATION 5
firstComponentValue = 1
secondComponentValue = 2
firstModelValue = 1
secondComponentValue = 2
}
Note, that valueChangeListener for the FirstInput was delivered to Apply Request Value phase (because of Immediate). All other phases don't have any changes.
Experiment #3. CommandButton has Immediate attribute:
And the Log:
actionEventListener {
currentPhase = APPLY_REQUEST_VALUES 2
firstComponentValue = null
secondComponentValue = null
firstModelValue = null
secondComponentValue = null
}
ValueChangeListeners and setters are not fired at all! Component values and Model values are null. They should be updated in Process Validations and Update Model phases, but they were skipped (key point #3). Very predictable result.
Experiment #4. FirstInput and CommandButton have Immediate attribute:
And the Log:
firstInputListener {
currentPhase = APPLY_REQUEST_VALUES 2
NewValue = 1
}
actionEventListener {
currentPhase = APPLY_REQUEST_VALUES 2
firstComponentValue = 1
secondComponentValue = null
firstModelValue = null
secondComponentValue = null
}
This is very important! Process Validations and Update Model phases are skipped as in the previous experiment because commandButton has Immediate , but valueChangeListener for FirstInput is fired because it has Immediate as well (key point #2). And value of the component (RichInputText) is updated too. So, when you click the "Immediate" button, valueChangeListeners for every "Immediate" input fire as well. Be aware of that! I've recently fixed some bug connected with that. And vice-versa, if you need in an actionListener of the "Immediate" button any components' values, set their Immediate to true.
What do you think will happen if Required of the FirstInput is set true?!
Experiment #5. FirstInput and SecondInput are required. FirstInput and CommandButton have Immediate attribute:
Let's leave inputs empty and click the button. In this case we don't have any log, we have the following:
Note! The button has immediate attribute set to true, but validation is failed. Because validation process for the FirstInput is moved up to Apply Request Values (Key Point #2). The myth is busted! On the other hand, the SecondInput is required as well, but there is no any problem with its validation. The SecondInput is not Immediate. It was going to be validated at Process Validation phase, but the phase was skipped because of Immediate button (Key Point #3). So, the myth is partly true :).
Experiment #6. FirstInput is autoSubmit. SecondInput is a partial target for the FirstInput:
Let's input something into the FirstInput and press Tab. We have validation exception:
Note! The FirstInput is Immediate, but we still have the exception. The myth is busted again. Processing of editableValueHolder components does not skip Process Validation phase. It doesn't skip any phase at all. (Key Point #2)
How to avoid this exception? We have to move up the lifecycle to the end manually. In the valueChangeListener we add the following:
And the exception's gone.
So, in order to use the Immediate attribute properly, remember three key points mentioned at the beginning of the post.
You can download my sample application and play with your own experiments.
Thank you!
I think, we should be aware of the following key points:
- Processing of any components with Immediate attribute is moved up to the Apply Request Values phase of the lifecycle. But there is a big difference in processing of editableValueHolder (inputText) and actionSource (commandButton) components.
- For EditableValueHolder components (like inputText) with Immediate attribute validation and valueChangeEvent delivering are done in Apply Request Values phase instead of usual Process Validation phase. This is the only change. The lifecycle is not stopped, it is going on, there is no any lifecycle phase skipping! Restore View->Apply Request Values->Process Validations->Update Model->Invoke App->Render Response
- For ActionSource components (like commandButton) with Immediate attribute, action event is delivered to Apply Request Values phase instead of usual Invoke Application phase. But!!! After that the lifecycle is jumping to the end, to Render Response phase. Validation and Model Update phases are skipped. Restore View->Apply Request Values->Render Response
In order to understand how these points actually affect the system's behaviour, I'm going to have some experiments with very simple form:
And the jspx for the form:
<af:form id="f1"> <af:inputText label="Label 1" id="it1" value="#{TestImmediateBean.firstInputValue}" valueChangeListener="#{TestImmediateBean.firstInputListener}" binding="#{TestImmediateBean.firstInputText}"/> <af:inputText label="Label 2" id="it2" value="#{TestImmediateBean.secondInputValue}" valueChangeListener="#{TestImmediateBean.secondInputListener}" binding="#{TestImmediateBean.secondInputText}"/> <af:commandButton text="commandButton 1" id="cb1" actionListener="#{TestImmediateBean.buttonActionListener}"/> </af:form>
I've added log messages to the valueChangeListeners, setters, and actionListener in order to follow the request process:
private void printCurrenPhaseID() { FacesContext fctx = FacesContext.getCurrentInstance(); Map requestMap = fctx.getExternalContext().getRequestMap(); PhaseId currentPhase=(PhaseId)requestMap.get("oracle.adfinternal.view.faces.lifecycle.CURRENT_PHASE_ID"); System.out.println("currentPhase = "+currentPhase); } public void firstInputListener(ValueChangeEvent valueChangeEvent) { System.out.println("firstInputListener {"); printCurrenPhaseID(); System.out.println("NewValue = " + valueChangeEvent.getNewValue()); System.out.println("}"); } public void secondInputListener(ValueChangeEvent valueChangeEvent) { System.out.println("secondInputListener {"); printCurrenPhaseID(); System.out.println("NewValue = " + valueChangeEvent.getNewValue()); System.out.println("}"); } public void setFirstInputValue(String firstInputValue) { this.firstInputValue = firstInputValue; System.out.println("firstInputValue setter {"); printCurrenPhaseID(); System.out.println("}"); } public void setSecondInputValue(String secondInputValue) { this.secondInputValue = secondInputValue; System.out.println("secondInputValue setter {"); printCurrenPhaseID(); System.out.println("}"); } public void buttonActionListener(ActionEvent actionEvent) { System.out.println("actionEventListener {"); printCurrenPhaseID(); System.out.println("firstComponentValue = " + firstInputText.getValue()); System.out.println("secondComponentValue = " + secondInputText.getValue()); System.out.println("firstModelValue = " + getFirstInputValue()); System.out.println("secondModelValue = " + getSecondInputValue()); System.out.println("}"); }
So, Experiment #1. Standard situation, nobody has Immediate attribute:
Immediate | AutoSubmit | Required | |
FirstInput | false | false | false |
SecondInput | false | false | false |
CommandButton | false | X | X |
Input values and click the button:
In the system log we can see the following:
firstInputListener {
currentPhase = PROCESS_VALIDATIONS 3
NewValue = 1
}
secondInputListener {
currentPhase = PROCESS_VALIDATIONS 3
NewValue = 2
}
firstInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
secondInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
actionEventListener {
currentPhase = INVOKE_APPLICATION 5
firstComponentValue = 1
secondComponentValue = 2
firstModelValue = 1
secondComponentValue = 2
}
Everything is good. ValueChangelisteners are delivered to the Process Validation phase, setters are fired at the Update Model phase and actionListener is delivered to the Invoke Application phase.
Experiment #2. FirstInput has Immediate attribute:
Immediate | AutoSubmit | Required | |
FirstInput | true | false | false |
SecondInput | false | false | false |
CommandButton | false | X | X |
And the log:
firstInputListener {
currentPhase = APPLY_REQUEST_VALUES 2
NewValue = 1
}
secondInputListener {
currentPhase = PROCESS_VALIDATIONS 3
NewValue = 2
}
firstInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
secondInputValue setter {
currentPhase = UPDATE_MODEL_VALUES 4
}
actionEventListener {
currentPhase = INVOKE_APPLICATION 5
firstComponentValue = 1
secondComponentValue = 2
firstModelValue = 1
secondComponentValue = 2
}
Note, that valueChangeListener for the FirstInput was delivered to Apply Request Value phase (because of Immediate). All other phases don't have any changes.
Experiment #3. CommandButton has Immediate attribute:
Immediate | AutoSubmit | Required | |
FirstInput | false | false | false |
SecondInput | false | false | false |
CommandButton | true | X | X |
And the Log:
actionEventListener {
currentPhase = APPLY_REQUEST_VALUES 2
firstComponentValue = null
secondComponentValue = null
firstModelValue = null
secondComponentValue = null
}
ValueChangeListeners and setters are not fired at all! Component values and Model values are null. They should be updated in Process Validations and Update Model phases, but they were skipped (key point #3). Very predictable result.
Experiment #4. FirstInput and CommandButton have Immediate attribute:
Immediate | AutoSubmit | Required | |
FirstInput | true | false | false |
SecondInput | false | false | false |
CommandButton | true | X | X |
And the Log:
firstInputListener {
currentPhase = APPLY_REQUEST_VALUES 2
NewValue = 1
}
actionEventListener {
currentPhase = APPLY_REQUEST_VALUES 2
firstComponentValue = 1
secondComponentValue = null
firstModelValue = null
secondComponentValue = null
}
This is very important! Process Validations and Update Model phases are skipped as in the previous experiment because commandButton has Immediate , but valueChangeListener for FirstInput is fired because it has Immediate as well (key point #2). And value of the component (RichInputText) is updated too. So, when you click the "Immediate" button, valueChangeListeners for every "Immediate" input fire as well. Be aware of that! I've recently fixed some bug connected with that. And vice-versa, if you need in an actionListener of the "Immediate" button any components' values, set their Immediate to true.
What do you think will happen if Required of the FirstInput is set true?!
Experiment #5. FirstInput and SecondInput are required. FirstInput and CommandButton have Immediate attribute:
Immediate | AutoSubmit | Required | |
FirstInput | true | false | true |
SecondInput | false | false | true |
CommandButton | true | X | X |
Let's leave inputs empty and click the button. In this case we don't have any log, we have the following:
Note! The button has immediate attribute set to true, but validation is failed. Because validation process for the FirstInput is moved up to Apply Request Values (Key Point #2). The myth is busted! On the other hand, the SecondInput is required as well, but there is no any problem with its validation. The SecondInput is not Immediate. It was going to be validated at Process Validation phase, but the phase was skipped because of Immediate button (Key Point #3). So, the myth is partly true :).
Experiment #6. FirstInput is autoSubmit. SecondInput is a partial target for the FirstInput:
Immediate | AutoSubmit | Required | |
FirstInput | true | true | true |
SecondInput | false | false | true |
CommandButton | true | X | X |
Let's input something into the FirstInput and press Tab. We have validation exception:
Note! The FirstInput is Immediate, but we still have the exception. The myth is busted again. Processing of editableValueHolder components does not skip Process Validation phase. It doesn't skip any phase at all. (Key Point #2)
How to avoid this exception? We have to move up the lifecycle to the end manually. In the valueChangeListener we add the following:
public void firstInputListener(ValueChangeEvent valueChangeEvent) { System.out.println("firstInputListener {"); printCurrenPhaseID(); System.out.println("NewValue = " + valueChangeEvent.getNewValue()); System.out.println("}"); //Jump to the Render Response phase FacesContext.getCurrentInstance().renderResponse(); }
And the exception's gone.
So, in order to use the Immediate attribute properly, remember three key points mentioned at the beginning of the post.
You can download my sample application and play with your own experiments.
Thank you!
Добрый день
ReplyDeleteОчень заинтересовался Вашим Блогом поскольку начал изучать Oracle ADF. Хотел уточнить для Себя нет ли возможности подключения переводчика на русский поскольку Я так понимаю статьи (заготовки) создаются на русском а далее на ангельский
Заранее благодарю
Статьи пишутся сразу на английском. Русского аналога нет. Можно попробовать переводчик Google, но получаеься такая каша ...
ReplyDeleteHi Eugene,
ReplyDeleteGreat work, hats off to you
I'm one of those guys who are scared to use immediate attribute of input components. I use it for cancel buttons to skip validations, but not for any other components.
ReplyDeleteYou've done a nice job explaining it with phases, but for the scared souls like me, can you explain what is the practical use of immediate attribute in input components. (like the use of immediate in cancel buttons to skip validations)?
Probably this post will be useful for you http://adfpractice-fedor.blogspot.com/2012/03/update-model-in-valuechangelistener.html
DeleteEXCELENTE POST !! Great Post !!
ReplyDeletegreat article man.. hats off to you
ReplyDeleteExcellent article!!! All my myths busted!
ReplyDeleteHi Eugene,
ReplyDeleteI have the following code in the Action Listener of a Command Button
BindingContext bc=BindingUtils.getBindingContext();
DCBindingContainer bCon=(DCBindingContainer)bindingContext.getCurrentBindingEntry();
OperationBinding operation=bc.getOperationBinding("Delete");
operation.execute();
I was getting Validations fired, even though I made Immediate as true for Command Button.Any Idea how to achieve this ?
Thanks
Nag
Hi Nag!
ReplyDeleteWhat type of validations are you getting fired? Do you have any immediate inputs on th page?
Nice Article...Learned a lot from it.. Thanks bro :-)
ReplyDeleteVery much informative...Thanks
ReplyDeleteGood eye opener on the phases and lifecycles, cherry on top is Immediate myth being busted :) thanks a lot.
ReplyDeleteExcellent, First time understood Adf life cycle so well
ReplyDeleteThanks a lot.
ReplyDeleteWhat a great explanation...hats off to u
Do you have any explanation on FacesContext?
ReplyDeleteNo, I don't. And what are you interested in?
DeleteThank you Mr. Eugene,
DeleteThis was very instructive.
Have a nice day.
Great Work keep it up!
ReplyDeletethanks...goord article and very informative and useful.
ReplyDeleteExcellent post...great job!!!
ReplyDeleteThanks and keep it up!!
Thanks, Excellent post.
ReplyDeleteI tried the experiment 6 and did not get any exception.. I am sure I have set the properties as mentioned exp 6.
Excellent post..Thanks
ReplyDeleteHi Suri, probably you forgot about "SecondInput is a partial target for the FirstInput"
ReplyDeleteThank you so much .. following the 1 can get clear understanding..
ReplyDeleteGreat post, finally understand how it works!!!
ReplyDeleteThank you
ReplyDeleteVery informative & useful article.