Skip to main content

ADF 12.2.x : Conditional Showing Message Instead of List of Values Popup

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 :

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


Popular posts from this blog

ADF 12.1.3 : Implementing Default Table Filter Values

In one of my projects I ran into a requirement where the end user needs to be presented with default values in the table filters. This sounds like it is a common requirement, which is easy to implement. However it proved to be not so common, as it is not in the documentation nor are there any Blogpost to be found that talk about this feature. In this blogpost I describe how to implement this.

The Use Case Explained
Users of the application would typically enter today's date in a table filter in order to get all data that is valid for today. They do this each and every time. In order to facilitate them I want to have the table filter pre-filled with today's date (at the moment of writing July 31st 2015).

So whenever the page is displayed, it should display 'today' in the table filter and execute the query accordingly. The problem is to get the value in the filter without the user typing it. Lets first take a look at how the ADF Search and Filters are implemented by the f…

ADF 11g Quicky 3 : Adding Error, Info and Warning messages

How can we add a message programatically ? Last week I got this question for the second time in a months time. I decided to write a short blogpost on how this works.

Adding messages is very easy, you just need to know how it works.
You can add a message to your faces context by creating a new FacesMessage. Set the severity (ERROR, WARNING, INFO or FATAL ), set the message text, and if nessecary a message detail. The fragment below shows the code for an ERROR message.

1: public void setMessagesErr(ActionEvent actionEvent) {
2: String msg = "This is a message";
3: AdfFacesContext adfFacesContext = null;
4: adfFacesContext = AdfFacesContext.getCurrentInstance();
5: FacesContext ctx = FacesContext.getCurrentInstance();
6: FacesMessage fm =
7: new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, "");
8: ctx.addMessage(null, fm);
9: }

I created a simple page with a couple of buttons to show the result of setting the message. When the butto…