Thursday, January 13, 2011

ADF query component ( <af:query> ) with a loading screen / spinner pop-up (Updated)


New updated alternative approach here 
The problem
I have an <af:query> component that displays query results in an <af:table> on my page. When the user hits the search button, I need to display loading screen that displays a loading animation and blocks user input.


The solution
Use  a pop-up to display a spinner while the query executes.


Before you proceed with implementing this approach,  there is another much much simpler possible solution. If displaying an animation to indicate that the query is being processed is all you need, then <af:statusIndicator> may be an option. It is extremely simple to use : Just drop the tag on to when you want the spinner to appear and you’re done. This however, does not prevent user input during the query execution and the spinner will be activated on all server calls. The pop-up approach detailed below is more complex but allows much more control over the whole process.

The rough idea is to :
  1. Intercept the Query/Search action in a backing bean.
  2. Launch a pop-up blocking user input and showing a loading message/spinner
  3. Fire the actual query to the database and close pop-up when query is done.

The finer detail is a bit more involved :
  1. Intercept the Query/Search action
Implement a query listener for the <af:query> component in your backing bean.
<af:query id="qryId1" 
          headerText="Search" disclosed="true"
          value="#{bindings.EmployeeSearchQuery.queryDescriptor}"
          model="#{bindings.EmployeeSearchQuery.queryModel}"                      
          resultComponentId="::resId1"
          queryListener="#{pageFlowScope.testBean.processQuery}"/>
public void processQuery(QueryEvent queryEvent)
  {
    setQEvent(queryEvent); //Save the query event for the method that really fires the query to use.
    toggleBusyPopup(true); //Fires a popup, which inturn fires a serverListener.
  }

  1. Launch the pop-up from the backing bean with Javascript :
public void toggleBusyPopup(boolean isShown){
    FacesContext context = FacesContext.getCurrentInstance();
    RichPopup popup = (RichPopup) JSFUtils.findComponent("busyPopup");
    ExtendedRenderKitService service =
      Service.getRenderKitService(context, ExtendedRenderKitService.class);
    if (isShown){
      service.addScript(context,
                        "var popup = AdfPage.PAGE.findComponent(\"" +
                        popup.getClientId(context) + "\"); popup.show();");
    }
    else{
      service.addScript(context,
                        "var popup = AdfPage.PAGE.findComponent(\"" +
                        popup.getClientId(context) + "\"); popup.hide();");
    }
    return;
  }

  1. Fire the actual query to the database and close pop-up when query is done
You need to fire the query and keep the pop-up open as long as the query executes.
  1. To fire the query, from within the pop-up, use an <af:clientListener> to trigger a Javascript function on the page.
  2. ...
    <af:popup id="busyPopup" clientComponent="true">
              <af:clientListener method="onPopupOpened" type="popupOpened"/>
    ...
    
  3. This Javascript method will in turn  trigger an <af:serverListener> inside the pop-up that will execute a method on the backing bean.
  4. <af:resource type="javascript">
            function onPopupOpened(event)
            {
              AdfCustomEvent.queue(event.getSource(), "popupOpenListner", 
              { }, false);
            }      
    </af:resource>
    
    <af:popup id="busyPopup" clientComponent="true">
              <af:clientListener method="onPopupOpened" type="popupOpened"/>
              <af:serverListener type="popupOpenListner"
                                 method="#{pageFlowScope.testBean.processSearchQuery}"/>
    
  5. The backing bean method will fire the actual query
  6. When the query is done, the same backing bean method will update the result table and hide the loading screen popup.
  7. public void processSearchQuery(ClientEvent clientEvent)
      {
        // Do query : //
        invokeMethodExpression("#{bindings.EmployeeSearchQuery.processQuery}",
                               Object.class, QueryEvent.class, getQEvent());
        AdfFacesContext.getCurrentInstance().addPartialTarget(JSFUtils.findComponent("resId1"));
        toggleBusyPopup(false);
      }
    



Running the example application.

You can download the application from here.
To run it, unzip the files and open the project in Jdeveloper.
Run the test.jspx by navigating to  ViewController > Web Content > test.jspx Right-Click > Run

No comments:

Post a Comment