Tuesday, May 5, 2009

How to force a table to refresh itself

Sometimes you will need to refresh a table after some event takes place in your backing bean.  You can refresh the table component using a partial trigger or refresh it from the backing bean using   getAdfFacesContext().addPartialTarget(uicomponent);  but that pulls the current values from the model through the page binding and doesn't necessarily pull fresh data from the database when the data has not been inserted from the ADF application.  I was having a problem in that when data was modified externally to our application (for example by an external web service call), that refreshing the table itself was not re-executing the query to bring in the new data.

This technique is simple and works across region boundaries instead of using contextual events.  Contextual events are Oracles' recommended practice for inter-region communication and I use them in many places but I have found them to carry a performance penalty, at least at the time of this writing and they are more difficult to configure.

When you have a table somewhere on your page (any region) that needs refreshing, perform the following steps


  • In a backing bean method, set a Boolean.TRUE into a requestScope variable. (see code in red below).
    ADFContext.getCurrent.getRequestMap()
    .put("refreshNeeded", Boolean.TRUE);
    This can be done from the backing bean of any region (task flow) running on the page.
  •  In the executable bindings for your table, add a invokeAction executable (named refreshIfNeeded) and bind it to the Execute or ExecuteWithParams action binding for your table’s view (on the left hand side).
  • Set the refreshIfNeeded binding to refresh “Always”
  • Add a refresh condition expression that references your request scope variable.






In your backing bean you could  retrieve the "Execute" Action from the bindings and executed it yourself but that only works if your backing bean is in the same region as the table you want to refresh.  Also, this technique assures the query is only executed once during the ADF lifecycle.   If the variable is set on requestScope more than once by different regions, no harm done.  You will still need to refresh the table component itself either through a partial trigger or from the backing bean as mentioned but this technique will force the query to execute as well.

7 comments:

  1. hey Don,

    You can force a table to refresh from your backing bean by writing:

    RequestContext.getCurrentInstance().addPartialTarget (FacesContext.getCurrentInstance().getViewRoot().findComponent('table-id'));

    But this is a ADF/Trinidad only feature...; perhaps your technique may work for other AJAX capable JSF libraries....

    Regards,
    Samba

    ReplyDelete
  2. but refreshing the UIComponent (UITable) won't necessarily re-execute the query to get fresh data which is what this accomplishes.
    Don

    ReplyDelete
  3. Hey Don,

    I’ll try to used your solution, but the problem was where to put “getRequestScope().put("refreshNeeded", Boolean.TRUE);” ?.

    Regards,
    Sunanda.

    ReplyDelete
  4. The code shown goes in a method in a backing bean that gets called by an event. It can be an action listener or action method or any other listener type method.

    ReplyDelete
  5. Hi

    This helped me in one of the functionalities.. thanks.

    However to get request map we can do - ADFContext.getCurrent.getRequestMap()

    Thanks
    Sachin

    ReplyDelete
  6. in ADF/Trinidad:
    DCBindingContainer container = (DCBindingContainer)EL.get("#{bindings}");
    DCIteratorBinding masterIterator = container.findIteratorBinding("ParentViewIterator");
    masterIterator.executeQuery();

    RowSet detailRowSet = (RowSet)((ParentViewRowImpl)masterIterator.getCurrentRow()).getDetailView();//or getAttribute("DetailView");
    detailRowSet.executeQuery();

    RequestContext.getCurrentInstance().addPartialTarget( FacesContext.getCurrentInstance().getViewRoot().findComponent('table-id') );

    ReplyDelete