April 8 2011

A customer I was working with this week had a need to do a customer search based on four different, but linked, critieria: Account, Countries, Accounts, and Contact.  First they'd pick an Account and then they would be presented with a list of Countries.  After selecting a Country, they'd pick one of the Accounts.  Finally, they'd pick the Contact.  The purpose if this post is to show you one technique to do this using popups.


I was recently presented with this issue and I that I knew a popup would fit, however it was going to require some work.

Based on the requirements, components were going to have to hide and show at each selection point. The actual flow requires four selections by the user, and each one is semi-dependent on the previous.

Multiple Display in one popup

 

My Design Thoughts

I could have used a popup with multiple master - detail tables where each table populates when the previous is selected and some 'magic' could be done to not show the tables until the previous is selected, however, due to constraints I went with four separate tables (actually three, I'll go into it later) that do some logic to evaluate their 'visible' attribute.

The client did not provide me with database access, but alternatively wrapped the database calls with web services (several in fact).  Some were simple, some were complex, however, a pragmatic approach using java to populate my view objects was necessary.   I used a page backing bean to make the appropriate web service calls and populate selected values.  (Of note, this may be more performant because it only populates necessary information to display rather than all the dependancies of 'unshown' components. *ponder*).

How I did it

The implementation consists of two components, java bean and jspx attributes.

The Java:

My backing bean contains a 'showScreen' integer.  I found that using an integer allowed me to have multiple states (as opposed to a boolean) and still make simple EL comparisons.  The showScreen value will be used in several attributes to control which tables are visible.    You must have an accessor for this value for it to be useful.

Here is the code for my backing bean:

 


 
public class PopupHelperBean { private int showScreen; public PopupHelperBean() { showScreen = 0; } /* Used on the Action attribute of the 'Cancel' button */ public String cancelButtonClick() { showScreen = 0; /* logic to 'clean' any temporary data */ return null; } /* Used on the Action attribute of the 'Ok' button */ public String okButtonClick() { AppModuleImpl am = (AppModuleImpl)getApplicationModule(); switch (showScreen) { case 0: /* Lookup 2 */ . . . // Call service 2 and populate VO break; case 1: /* Lookup 3 */ . . . // Call service 3 and populate VO break; case 2: /* Lookup 4 */ . . . // Call service 4 and populate VO break; case 3: /* Finish. */ /* do logic to 'save' data selected. Use the getApplicationModule */ /* method to retreive the data control, and gain access to VO */ . . . /* Close Popup */ RichPopup rp = (RichPopup)getComponent("popupWindowId"); rp.hide(); break; default: /* shouldn't be here... throw error */ break; } if (showScreen >= 3) { showScreen = 0; } else { showScreen++; } return null; } private UIComponent getComponent(String componentId) { FacesContext facesCtx = FacesContext.getCurrentInstance(); return facesCtx.getViewRoot().findComponent(componentId); } public AppModuleImpl getApplicationModule() { FacesContext context = FacesContext.getCurrentInstance(); ValueBinding vb = context.getApplication().createValueBinding("#{data}"); BindingContext bc = (BindingContext)vb.getValue(context); DCDataControl dc = bc.findDataControl("AppModuleDataControl"); return (AppModuleImpl)dc.getDataProvider(); } /* needed for access on the screen */ public int getShowScreen() { return showScreen; } }

Using an action attribute on the buttons I created, I call the 'okButtonClick' or alternatively the 'cancelButtonClick' method.  Then with a switch case statement I would make the appropriate web service call.  Check out the getComponent method, this allows me to finally get my popup component (note it is necessary to type cast your return element) and eventually 'hide' it.  This can be used for any properties you need to retrieve from other UI elements.  The cancel button must reset the showScreen unless you desire the user to remain on the same screen they left off at.  On case three, you would save any information that was acquired and repopulate the main screen if necessary.

If you notice, the first service call is not included here, this is handled by the parent page bean, using an action listener on the popup component. 


 
public void popupFetchListener(PopupFetchEvent popupFetchEvent) { // Call web service 1 // No code is necessary to 'open' the popup, that is handled by showPopupBehavior tag on the button. }

Jspx Attributes

I designed a single popup that contains a panel window.  The panel window has four components, three Panel Headers, and a Panel Group Layout (for buttons).

Each of the panel headers contains a table, and the panel group layout has two buttons.

Design

The reason there are only three tables is that one is reused for an address component.  I change the title of the panel header from 'Select account' to 'Select Account to use for Lookup' with the following EL:

#{uiBundle.SELECT_ACCOUNT} #{pageFlowScope.popupHelper.showScreen == 0 ? " to Send to Lookup:" : ":"}

Now each of the tables need to be displayed individually, however some also need to be updated based on the results of the other.

Each of the panel headers has a visable attribute that needed to be changed (replace : 0,1,2...) Also, on the panel headers, you need to set a partial trigger so that the component will refresh (You only need to trigger on the 'ok' button, because the cancel button will close the dialog box).
- visible: #{pageFlowScope.popupHelper.showScreen==0}
- partial trigger: okbutton
 

Also of note, the partial submit attribute should be true for the 'ok' button, until the last screen is shown.  For the cancel button it will be false the whole time.  The reason for this, is we want to do a full submit, when the popup is supposed to 'hide'.  Until then, we only want a partial submit.  For the 'ok' button, the partial submit should evaluate an EL expression that looks like:

Recap

Once the popup is displayed (and the first webservice is called), link the 'OK' button to a backing bean that increments a integer variable.  Your panels will use that variable to decide when to show.  They will also need to trigger on the OK button so that they re-evaluate their visible expression.  Once on your final screen, change the partial submit attribute on the button to false so that the popup finishes its execution!

Thats it!

About the Author

Bio

Kevin has over 9 years experience in enterprise scale implementations. Kevin is very experienced in architecting, modeling and developing BPM processes, particularly those requiring advanced ADF UI screens, as well as setting up the infrastructure for the BPM solutions, which have integrated with multiple external systems.

Join the Conversation

Enter your first name. It will only be used to display with your comment.
Enter your email. This will be used to validate you as a real user but will NOT be displayed with the comment.
By submitting this form, you accept the Mollom privacy policy.