Facelets and PanelDashboard Gotchya
By Duncan Mills-Oracle on Jul 20, 2012
An issue that came to light via the ADF Expert Methodology Group last week seemed to be an interesting one to write about. Olga had logged an issue in the EMG Issue Tracker which seemed to imply that there was a difference in behaviour between JSP and Facelets when using the <af:panelDashboard> component. Specifically, the problem seemed to be that when you made changes to the ordering of the panels within the dashboard, under JSP the order would be remembered, but the same page and code under Facelets (ADF 11.1.2.n) the change would be ignored.
This naturally piqued my interest and the detective work began. Running the test-case that Olga had thankfully supplied and then that our own Chris Muir had further simplified, the problem certainly reproduced, so it seemed to be real, however, reading the code there was a clue to what was going on. Olga had sensibly adapted the panelDashboard sample from the ADF Demo WAR file and the code in question that was called when the relevant operation took place contained an interesting comment. Here it is:
// Apply the change to the component tree immediately: // Note that the ChangeManager.addComponentChange() is required here if using Facelets because the Facelets view // handler will discard the change in order when the next render phase is started. ComponentChange change = new ReorderChildrenComponentChange(reorderedIdList); RequestContext rc = RequestContext.getCurrentInstance(); rc.getChangeManager().addComponentChange(FacesContext.getCurrentInstance(), dashboard, change); change.changeComponent(dashboard); ...
So there was an admission here that something slightly different was going on in the world of Facelets. Furthermore, reading the tagdoc on panelDashboard gives a bit more information and a further clue:
You may wish to use org.apache.myfaces.trinidad.change.ReorderChildrenComponentChange and ChangeManager().addComponentChange() if you wish to preserve a new ordering of the children. Ideally, you should use an MDS ChangeManager to persist the change longer than the user's session so that the use does not have to repeatedly make the same reorder gesture every time your application is used.
ChangeManager is the magic word here. Back to the testcase, and this time I ran it in debug with the specific aim of inspecting the change Manager instance returned by the rc.getChangeManager() call above. Sure enough, there was the problem. In the testcase, the ChangeManager was an instance of org.apach.myfaces.trinidad.change.NullChangeManager. If you have a look at the source code for that you'll see that all the operations are No-Ops, so it's not surprising that the change was not persisted.
So the solution was then obvious, in the project properties, in the ADF View panel, check the Enable User Customizations box to configure a Change Manager (either Session or MDS) that actually does something and the problem is resolved.
The morals of this story are to read the doc and do not be afraid of debugging code. It will really help, of course, if you make sure you have the ADF Source Code installed (just ask Oracle Support). You can save a lot of time, frustration and needless SRs.