Creating a dynamic (AJAX) Column Footer Summary in a Table Component using ADF Faces

One fairly common requirement for web applications is the display of Summary fields with calculated values. An obvious example is a table of multiple records with column-summaries appearing underneath the table. Using ADF Faces technology, it is fairly simple to quickly develop an application that presents a multi-record layout based on data retrieved from a database. In this article we will see how we can add a summary column to the columns in such a table layout – and to make those summaries automatically updating when a value for one of the records in the table is changed in the specific column.

Creating a dynamic (AJAX) Column Footer Summary in a Table Component using ADF Faces adfFacesColumnSummary 

....
 

Creating the master-detail application

The application I use for this example is a well-known one: a Master-Detail (form-table) page for DEPT and EMP. For each Department, we will see the details in a Form layout with underneath a Table component with all Employees in the Department.

Using JDeveloper 10.1.3, ADF BC, ADF Binding (Framework) and ADF Faces, creating such a page is almost trivial, especially if you generate it using JHeadstart 10.1.3 (which is what I did here). In quick summary the steps:

  1. Create new Application, choose Web Technology (ADF BC and JSF) as Technology Template; Model and ViewController project are created automatically
  2. Create Business Components from Tables EMP and DEPT in SCOTT schema in the Model project; add View Link from Source DeptView to Target EmpView
  3. Using JHeadstart: enable JHeadstart on the ViewController project, create default Application Definition file, generate the application
  4. Without JHeadstart: create a new JSF JSP page, drag and drop DeptView from the AppModule DataControl as Editable Form, drag and drop EmpView2 under DeptView to the jspx page.
  5. Run the Application to verify the data is shown and the master-detail coordination works as expected.

 

Add the Salary Summary to the DeptView ViewObject

We want this page to also contain the summary of all salaries in the currently selected Department. Where to put it is a later concern, let’s first get it on the page in the first place. The steps for this are:

1. Select the DeptView ViewObject and select Edit from the RMB menu

2. On the Attributes tab, press the New button to create a new (transient) attribute called SalSum. SalSum is of type Number, is Never updateable and is not mapped to Column or SQL

3. On the Java Tab, check the checkbox Generate Java File under View Row Class DeptViewRowImpl; also check the Accessors checkbox:
Creating a dynamic (AJAX) Column Footer Summary in a Table Component using ADF Faces adfFacesColumnSummaryVOWiz

4. Press OK to close the VO Editor wizard.

5. From the RMB menu on the DeptView VO, select the option Goto View Row Class

Creating a dynamic (AJAX) Column Footer Summary in a Table Component using ADF Faces adfFacesColumnSummaryGotoRow 

Locate the Number getSalSum() accessor method. Replace the default implementation with this one:

  public Number getSalSum() {
RowIterator emps = getEmpView();
Number sum = new Number(0);
while (emps.hasNext()) {
sum = sum.add( (Number)emps.next().getAttribute("Sal"));
}
return sum;
}
 

6. Add the SalarySum to the page either by dragging and dropping it from the DataControl Palette, or by synchronizing the Dept Group in the JHeadstart Application Definition editor and regenerating the application.

7. Run the application to inspect the newly added SalarySum. It should contain the correct sum of the salaries in the currently selected department. If you change the value of one of the salaries, it will currently not be updated automatically when you leave the field. It will however be synchronized for example when you sort the table by clicking one of the sortable column headers. 

Creating a proper Column Footer with the Salary Summary

The next step is to create the proper layout for the Salary Summary: we want to have it displayed in Footer underneath the Salary Column in our Table Component. The ADF Faces Column Component has a so called footer facet. We can use that facet to specify whatever should be rendered underneath the column. So we can implement the column footer facet for the Salary column, containing the SalarySummary, much like this:

 <af:column sortable="true" noWrap="true"
sortProperty="Sal" formatType="number">
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Salary"/>
</h:panelGroup>
</f:facet>
<af:inputText id="DetailEmpSal" value="#{row.Sal}"
required="#{bindings.DetailEmpSal.mandatory}"
rows="#{bindings.DetailEmpSal.displayHeight}"
columns="#{bindings.DetailEmpSal.displayWidth}"
maximumLength="10" >
<f:convertNumber groupingUsed="false"
pattern="#{bindings.DetailEmpSal.format}"/>
</af:inputText>
<f:facet name="footer">
<h:panelGroup>
<af:outputText id="DeptEmpSalSum"
value="#{bindings.DeptEmpSalSum.inputValue}">
<f:convertNumber groupingUsed="false"
pattern="#{bindings.DetailEmpSal.format}"/>
</af:outputText>
</h:panelGroup>
</f:facet>
</af:column>
 

However, it turns out that the Column Footer Facet gets only rendered if also the Table’s Footer Facet has been specified. Otherwise, the column footer is simply not rendered! The table footer facet is like this:

  <f:facet name="footer">
<af:outputText id="deptEmpTableFooter" value="Summary:"/>
</f:facet>
</af:table>
 

Dynamically Refreshing the Salary Summary whenever a Salary value is changed

At this point, when the page is loaded, the Summary is displayed as expected. However, if we change the value of one of the salaries, the Summary is not immediately updated. Only when the table is refreshed for some other reason, such as sorting or detail disclosure/hide will the new summary value be shown. What we would like to have is an immediate update of the Summary whenever one of the Salaries is changed. We want to leverage the Partial Page Refresh mechanism of ADF Faces to help us realize this.

1. Let’s ensure that a change in a salary value causes an immediate submit to the server. We can easily do that by setting the autoSubmit attribute for the DetailEmpSal inputText component to true:

   <af:inputText id="DetailEmpSal" value="#{row.Sal}"
required="#{bindings.DetailEmpSal.mandatory}"
rows="#{bindings.DetailEmpSal.displayHeight}"
columns="#{bindings.DetailEmpSal.displayWidth}"
maximumLength="10" autoSubmit="true">
<f:convertNumber groupingUsed="false"
pattern="#{bindings.DetailEmpSal.format}"/>
</af:inputText>
 

2.  In order to update the SummarySal element upon processing the Partial Page Refresh caused by the Sal
ary change, we have to set the partia
lTriggers attribute.

However, here we run into a problem: we need to specify the ID of the component whose change should trigger the update of the Salary Sum in the partialTriggers attribute for the DeptEmpSalSum component. However, even though the ID for the Salary inputText component seems simple enough – DetailEmpSal – this is not the correct value! Since the Salary field will appear not once but once for every record in our table component and the ID needs to be unique, the actual ID will be different. Each Salary field will have an ID that includes DetailEmpSal as well as :0, :1, :2 etc. to make the ID values unique.

When I try a temporary workaround, just to see whether things are working, I run into a second issue: if I specify partialTriggers="DeptEmpDname" for the Column Footer Facet and/or the Table Footer Facet, it turns out that they are not in fact refreshed when PPR is processed. Only when I specify partialTriggers="DeptEmpDname" at the level of the Table Component will the Footer Facets be properly updated as part of the PPR processing cycle. That means I have now achieved that whenever I change one or more Salary values and I subsequently change the Department Name in the Master record, I get ‘dynamic, instantaneous’ update of the Salary Sum. Almost there, but not quite. By the way: what I am doing here is somewhat similar to Frank Nimphius’ blog article: ADF Faces: Compute totals of selected rows in a multi select table. He does not seem to have a problem with Table Footer refresh so perhaps I am doing something wrong here.

After consulting Frank and Duncan Mills, I am pointed in a new direction: programmatically specifying the targets of Partial Page Refresh. Sounds interesting, let’s try it out:

Programmatically specifying the targets of Partial Page Refresh

There is an API call – AdfFacesContext.getCurrentInstance().addPartialTarget(<the component to
refresh>); – that Duncan suggested to me. This should allow me to specify the Table – or perhaps even the Table and/or Column Footer Facet – that should be refreshed as part of the current PPR cycle. Of course, this call should be made whenever Salary has been changed. So using a ValueChangeListener, I should be well on my way. Should I not?

1. Create a new class EmpMgr to handle the Salary Changed event by adding the EmployeeTable to the list of partial targets:

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;

import oracle.adf.view.faces.context.AdfFacesContext;

public class EmpMgr {
public EmpMgr() {
}

public void HandleSalaryChangeEvent(ValueChangeEvent valueChangeEvent) {
Application app = FacesContext.getCurrentInstance().getApplication();
UIComponent table = (UIComponent)app.createValueBinding("#{DetailEmpCollectionModel.table}").getValue(FacesContext.getCurrentInstance());
AdfFacesContext.getCurrentInstance().addPartialTarget(table);

}
}
 

Note: the table component had its Binding property already set to #{DetailEmpCollectionModel.table}; I am simply reusing that binding. Also note that trying to use the DeptEmpSalSum outputText as partialTarget did not lead to a refresh: I had to use the table as refresh target.

2. Configure this new class as Managed Bean 

    ...
<managed-bean>
<managed-bean-name>EmpMgr</managed-bean-name>
<managed-bean-class>EmpMgr</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
 

3. Specify a ValueChangeListener for the Salary field in my Employees table:

<af:inputText id="DetailEmpSal" value="#{row.Sal}"
required="#{bindings.DetailEmpSal.mandatory}"
rows="#{bindings.DetailEmpSal.displayHeight}"
columns="#{bindings.DetailEmpSal.displayWidth}"
maximumLength="10" autoSubmit="true"
valueChangeListener="#{EmpMgr.HandleSalaryChangeEvent}">
<f:convertNumber groupingUsed="false"
pattern="#{bindings.DetailEmpSal.format}"/>
</af:inputText>
 

4. Run the application, change a salary and keep your fingers crossed… IT WORKS!!!! (Thanks Frank and Duncan). Note: this opens up a lot of very interesting possibilities: we can determine dynamically on the server side which fields to be updated in the browser by simply adding them to the list of partialTargets.

Resources

ADF Faces: Compute totals of selected rows in a multi select table – Frank Nimphius’ Blogbuster Weblog 

ADF Faces Components Index on OTN 

ADF Faces Apache Incubator –  AF:Column component

Subversion Sources Repository for Apache MyFaces (Incubator) ADF Faces Sources 

 

6 Comments

  1. Ravi October 5, 2011
  2. narayan August 7, 2010
  3. Erik March 19, 2007
  4. S. Gülçin Yücel March 14, 2007
  5. Shirley March 7, 2007
  6. Lucas Jellema August 18, 2006