In a previous post we looked at how to get the selected value or text from an LOV in our backing bean. While that approach is effective for most use-cases, there are some situations where you cannot use that approach , or using it is too cumbersome.In this post we'll look at a slightly different approach that can be used when you need to use the ID or String value in EL or you don't want to have a backing bean.
Needing to get the value in EL can be a common scenario when you are trying to extract the selected ID behind an LOV and pass it somewhere, like passing the ID to another bounded task flow. Very common use-case.
To consider an example, suppose you have an app built on the EMP and DEPT tables and you have an "Employee Edit" page. On this page, you have the department the employee belongs to, implmented as a drop down list of Departments ( LOV ). Now lets say you want to have an "Edit Dept" button next to the Department drop-down, which when clicked should take you to an "Edit Dept" page for the currently selected dept in the drop-down. This LOV drop-down on the "Edit Employee" page was created on the EMP VO, for the Dept ID attribute and would use a view-accessor on the EmpVO to map the Dept ID in the Emp Table with the Dept ID in the Dept table and pull the Dept name from that. What we need is the Department ID of the department selected in the drop-down so that we can pull up that department details in the "Department Edit" page when the user selects a department and clicks on "Edit Department".
The LOV when placed on a page, creates a list binding in the binding layer by default and as such is not very useful for us because it stores the index of the selected Item and not the underlying ID. The easiest method to get the current ID is to manually create an Attribute Binding in the pageDef that points to the DeptID on the same iterator that the list binding is pointing to. This means that when the user changes the selection in the LOV, the currency in the iterator will change and since the attribute binding is on the same iterator, the attribute binding will also change.
To get the value in EL, just refer to the attribute binding in EL and not the list binding .
Simple !
Sample application
I'm gonna re-use the sample application from the ADF Security Basics post. http://myadfnotebook.googlecode.com/files/SecurityDemo.zip
In this application, there is a feature when editing an employee there is s a link go to the edit department page (from the employee detail page). This page has a drop down allowing you to select a department for the employee, but also a link on the side directly takes you to edit the selected department. This link is implemented using the approach described here.
This is an update/follow up on a previous post about displaying a spinner or loading screen while a query is in progress. The original post can be found here. This post describes a much simpler method for getting the same result, and addresses a flaw in the original approach. Its a direct adaptation of the method described by Frank Nimphius in the ADF Code corner example.
The general idea here is to
Execute a custom piece of JavaScript code when user hits 'Search' in af:query
In our JS code, add a busy state listener to fire when the busy state changes.
Show hide a popup based on the busy state.
The ADF code corner example is based on a normal command button. But in the case of af:query we don't have access to the command button that fires the search. In this case, we can use a Client Listener on the af:query that fires on the "query" event. like below :
The example above, the JavaScript function 'fireLoadingScreen' is a custom JavaScript function and is implemented as below :
function fireLoadingScreen(evt){
var source = evt.getSource();
var popup = AdfPage.PAGE.findComponentByAbsoluteId('spinnerPopUp');
if(popup != null ){
AdfPage.PAGE.addBusyStateListener(popup,handleBusyState);
evt.preventUserInput();
}
}
function handleBusyState(evt){
var popup = AdfPage.PAGE.findComponentByAbsoluteId('spinnerPopUp');
if(popup!=null){
if (evt.isBusy()){
popup.show();
}
else if (popup.isPopupVisible()) {
popup.hide();
AdfPage.PAGE.removeBusyStateListener(popup,handleBusyState);
}
}
}
The complete example can be downloaded from here : http://myadfnotebook.googlecode.com/files/LoadingScreen2.0.zip
function fireLoadingScreen(evt){
var source = event.getSource();
var popup = source.findComponent('spinnerPopUp');
if(popup != null ){
popup.show();
}
This uses the source UIComponent object to search for the popup component (not from the root of the document). So the search is relative to the af:query. But now you'd have no way to close the popup but to use the old backing bean based method though.
For those interested, lets look at what was actually wrong with the original approach ( other than being very convoluted )...
While the original post still holds good for a lot of situations (especially if you use jsff and are deploying the app as a portlet) , when specifically applied to af:query, it has one drawback that I recently came to know about. This drawback will be apparent only in a load balanced and highly available environment. If you use a load balancer or fail-over system, there is a chance that the user's session might be migrated to another server while a query is happening. When that happens, things go bad if you use the approach detailed in the older post . Lets see why.
Flow of events for the original solution
The original approach can be applied generically to normal command components or af:query (though there are other more declarative ways of doing this with command components). This method is useful if upon clicking a command component if you want to execute some logic in your backing bean to choose a specific pop-up/loading screen (our customers wanted different types of loading screens for various types of input specified by the user in the af:query ). What makes the implementation different for af:query vs. a normal command button is that in af:query you don't have direct access to the search button (unlike a normal command button). So we had to intercept the search action and hook in to it (step 1). The idea was to fire a user-input blocking pop-up by hooking in to the search action (but not start the search yet) (step 2), then fire the actual search from the pop-up once its visible (steps 3,4,5). In doing that we had to save the QueryEvent as a member variable in the backing bean. The query event would be provided to the method registered as the query listener (step 1), but since we don't actually do the query from that method(we just launch the popup from here) and fire the actual query from the pop-up using a second method(step 6), we need to make the QueryEvent object available to that second method (in step 6). And this is not a simple case of calling one method and passing a parameter to it. The second method is invoked using java script code to call back the backing bean. So we had to store the QueryEvent object and guess what, it is not Serializable ! So in the event a session is replicated, the serialization of the backing bean would fail. At this point one might think we can just make the QueryEvent transient and get around the serialization; But that door opens to a far more subtle and dangerous problem. Suppose we mark it transient, and there are a thousands of users on our load balanced system executing searches. The load balancer decides to move some sessions across to another server, and lets say it just happens to pick a session where a user just clicked the search button, and we've just saved the QueryEvent and opened our loading screen popup (step 1 complete). The entire session is serialized and replicated on the second server, and when the session is inflated on the second server, since we marked the QueryEvent as transient , the QueryEvent is set to null on the second server. Now things resume, but when we reach step 6 in our flow, instead of seeing a QueryEvent we would see null. What makes this problem really frustrating is that since the load balancer is involved, the issue will not be easily reproducible and it would be very difficult to trace.
As you can see, the new solution makes the whole process a lot simpler actually. As long as you have a single loading screen you want to display whenever the search button is hit, this solution is much better that the original one.
Earlier I had discussed about how to inject and fire JavaScript on the browser from a backing bean. This is an invaluable tool for some corner cases and can make life easier , however there is a gotcha here when we try to do things in a sequential order after we inject the JavaScript. Its a subtlety that would go unnoticed for most people too, so that makes it noteworthy since knowing this can save us a lot of time spent on debugging.
The code when set as the action for a command component will execute and display a browser dialog with the message "Calling Service". Lets say there are things you want to execute after the browser popup is launched like call a WebService or execute a query. So you make the code :
FacesContext fctx = FacesContext.getCurrentInstance();
StringBuilder script = new StringBuilder();
script = script.append("alert('Calling Service');");
Service.getRenderKitService(fctx,ExtendedRenderKitService.class)
.addScript(fctx,script.toString());
WebServiceProxy.invokeWSmethod() // assume we call a synchronous WebService.
What one might expect to happen is for the message to appear and the web service to be invoked(in that order). But what happens in reality is that the web service is invoked and then the message appears.This is because the java script is executed only after the method finishes (I'm assuming here that it executes when the call stack is empty). This order of execution is relevant when we want to use JavaScript to show a dialog or an af:popup that should be triggered first(executed on the browser) before executing an operation. The obvious solution for this would be to split up the javascript and the actual operation in to two methods on your backing bean and just run the JavaScript part first and then use a call back method in your backing bean (using a server listener) to run the actual operation. A good example of how this can be done in practice is described here.