Problem
We have a swing-based front-end for an enterprise application, and now it implements (at the moment a simpler) JSF / Seam / Richfaces interface for it.
Some pages contain fields that, when edited, should lead to changes in other fields. We need this change to be displayed to the user immediately (i.e. they did not need to press a button or anything else).
I successfully implemented this with h:commandButton and adding onchange="submit()" to the fields, which cause other fields to change. Thus, the form view appears when they edit the field, and the rest of the fields are updated as a result.
This works fine, but especially when the server is under heavy load (which often happens), the presentation form can take a lot of time, and our users continue to edit the fields at the same time, which are then returned when responses to processed onchange="submit()" requests onchange="submit()" .
To solve this problem, I was hoping to achieve something where:
- When editing a field, if necessary, only this field is processed , and only those fields that it changes are changed again (so that any other changes made by the user at the same time are not lost).
- At the click of a button, all fields are processed and re-displayed as usual.
Solution (unstable)
Well, I think it would be easier to show a little of my page first. Please note that this is only an excerpt and that on some pages there will be many fields and many buttons.
<a4j:form id="mainForm"> ... <a4j:commandButton id="calculateButton" value="Calculate" action="#{illustrationManager.calculatePremium()}" reRender="mainForm" /> ... <h:outputLabel for="firstName" value=" First Name" /> <h:inputText id="firstName" value="#{life.firstName}" /> ... <h:outputLabel for="age" value=" Age" /> <h:inputText id="age" value="#{life.age}"> <f:convertNumber type="number" integerOnly="true" /> <a4j:support event="onchange" ajaxSingle="true" reRender="dob" /> </h:inputText> <h:outputLabel for="dob" value=" DOB" /> <h:inputText id="dob" value="#{life.dateOfBirth}" styleClass="date"> <f:convertDateTime pattern="dd/MM/yyyy" timeZone="#{userPreference.timeZone}" /> <a4j:support event="onchange" ajaxSingle="true" reRender="age,dob" /> </h:inputText> ... </a4j:form>
Changing the age value changes the dob value in the model and vice versa. I use reRender="dob" and reRender="age,dob" to display the changed values ββfrom the model. It works great.
I also use the global queue to ensure the order of AJAX requests.
However, the onchange event onchange not occur until I click somewhere else on the page or click on a tab or something else. This causes problems when the user enters a value, such as age , and then clicks calculateButton without , by clicking elsewhere on the page or by clicking a tab.
The onchange event seems to be the first to happen, as I see the dob change, but these two values ββare then returned when the calculateButton query is executed.
So, finally, to the question: is there a way to make sure that the model and view are completely updated before the calculateButton request is made so that it does not return them? Why is this not happening as I use the AJAX queue?
Workarounds
There are two strategies to get around this limitation, but they both require bloat in Facelet code, which can confuse other developers and cause other problems.
Workaround 1: Using a4j: support
This strategy is as follows:
- Add the
ajaxSingle="true" attribute to calculateButton . - Add the
a4j:support tag with the ajaxSingle="true" attribute in firstName .
The first step ensures that calculateButton does not overwrite values ββin age or dob , since it no longer processes them. Unfortunately, it has a side effect that it no longer processes firstName . The second step is added to counter this side effect by processing firstName before pressing calculateButton .
Keep in mind that there can be more than 20 fields, such as firstName . The user filling out the form can then call 20+ requests to the server! As I mentioned, this is also bloated, which can confuse other developers.
Workaround 2: Using the process list
Thanks to @DaveMaple and @MaxKatz for suggesting this strategy, it looks like this:
- Add the
ajaxSingle="true" attribute to calculateButton . - Add the
process="firstName" attribute to calculateButton .
The first step is achieved in the same way as in the first workaround, but has the same side effect. This time, the second step ensures that firstName processed using calculateButton when clicked.
Again, keep in mind that 20 fields, such as firstName , can be included in this list. As I mentioned, this is also bloated, which can confuse other developers, especially since the list should contain some fields, but not others.
Age and DOB Setters and Getters (just in case they are the cause of the problem)
public Number getAge() { Long age = null; if (dateOfBirth != null) { Calendar epochCalendar = Calendar.getInstance(); epochCalendar.setTimeInMillis(0L); Calendar dobCalendar = Calendar.getInstance(); dobCalendar.setTimeInMillis(new Date().getTime() - dateOfBirth.getTime()); dobCalendar.add(Calendar.YEAR, epochCalendar.get(Calendar.YEAR) * -1); age = new Long(dobCalendar.get(Calendar.YEAR)); } return (age); } public void setAge(Number age) { if (age != null) {