Today I had to fix an issue in one of our ADF applications. For those of you working with ADF (as I do too occasionally) this might be valuable, so I decided to share this. The application is based on an old Oracle Forms applications and I had to implement the following functionality:
FRM-41830 : List of Values contains no entries.
In other words (more ADF like) : If you are about to render a List of Values and that List of Values contains no rows, just show a message instead of showing the List of Values.
In this post I will describe how I was able to implement this although it turned out to be a challenge.
Below are the steps that I took. Note that the Application can be downloaded from GIT.
1) I use a UI pattern where we use readonly table and do edits in a popup. That means that any List of Values will add an additional popup to the UI.
2) The List of Values that I use in this sample only shows entries where the minimum salary is less then the employees current salary. Not that that makes any sense, but for the this blogpost it works fine.
3) There is a business rule stating that I need to raise an exception when the LOV is invoked with no rows. This is actually where things get tricky. First of all I need to implement a launchPopupListener on the inputListOfValues. This method will fire when the popup is invoked. There I can also do all the handling that I need, such as setting the bind variable based on the salary of the selected user (line 6), and determining how many rows will be returned by the list of values (line 9) and finally throwing the exception if 0 rows are found (line 14).
1: public void openJobsLov(LaunchPopupEvent launchPopupEvent) {
2: // Add event code here...
3: BindingContainer bindings = getBindingContainer();
4: FacesCtrlLOVBinding lov = (FacesCtrlLOVBinding) bindings.get("JobId");
5: BigDecimal bCurrentSalary = (BigDecimal) evaluateEL("#{bindings.Salary.inputValue}");
6: lov.getListIterBinding()
7: .getViewObject()
8: .setNamedWhereClauseParam("BCurrentSalary", bCurrentSalary);
9: Number rowsFetched = lov.getListIterBinding()
10: .getViewObject()
11: .getEstimatedRowCount();
12: if (rowsFetched.intValue() == 0) {
13: try {
14: Exception ex = new Exception("EMP-00001 : No jobs within your salary range");
15: throw (ex);
16: } catch (Exception e) {
17: FacesContext fc = FacesContext.getCurrentInstance();
18: fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Info", e.getMessage()));
19: }
20: }
21: }
When I run the application now, the exception is thrown, the message is shown to the user, but unfortunately the whole UI is blocked and never released. This is due to the List of Values popup which was supposed to be rendered, but due to the exception, never is really visible. This leaves the user with a UI that can no longer be accessed. It is completely irresponsive......
And it stays like this, regardless of what you do. By hitting enter, I can close the message, and by escape I can close the edit popup, but the main UI stays complete locked..... bummer.....
As mentioned before the probable cause is the LOV that is not rendered, but somehow the modality is added to the UI. So what if we could prevent the Popup from rendering? There have been extensive blogposts about this, summarised in or derived from this forum thread : https://community.oracle.com/thread/2598020
The main thing in this is the use of the following piece of code in the launchPopupListener
1: launchPopupEvent.setLaunchPopup(false);
This will prevent the popup from being launched. However, this will not work if the user clicks the LOV icon.... The other option is to shortcut the JSF lifecycle by jumping directly to reponseComplete phase.
1: FacesContext.getCurrentInstance().responseComplete();
This works, but it will also prevent us from showing the message to the user as this is not added to the response.
So, are there any other options? Obviously yes, otherwise I had no reason to write this blogpost. Here is what I did.
4) The solution:
As by now I figured out what the cause of the issue was, and I had no ways of preventing the LOV popup from showing, I figured I tried to hide it, as I would do with any regular popup. First I need to find the popup itself in the componentRoot. Once it is found, I can simply hide it.
The way that the ID of the LOV popup is constructed by ADF is simple. It takes the ID of the inputListOfValues component (in my case jobIdId) and it suffixes it with "_afrLovDialogId".
So here we have it. Find the LOV (line 8) and hide it (line 9).
1: if (rowsFetched.intValue() == 0) {
2: try {
3: Exception ex = new Exception("EMP-00001 : No jobs within your salary range");
4: throw (ex);
5: } catch (Exception e) {
6: FacesContext fc = FacesContext.getCurrentInstance();
7: fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Info", e.getMessage()));
8: RichPopup lovPopup = (RichPopup) findComponentInRoot("jobIdId_afrLovDialogId");
9: hidePopup(lovPopup);
10: }
11: }
Note that the functionality for hidePopup and findComponentInRoot are available in my GIT sample.
When I run the application now, it works as expected. The message is not grayed out, neither is the edit Popup. I can click Ok on the message, I can click Save or Cancel on the edit Popup, and the main UI is responsive again.
That's all for now.
The code can be downloaded here from GIT
Comments