While I was checking the new Oracle Alta UI Demo (http://jdevadf.oracle.com/workbetter/faces/index.jsf) I noticed a dashboard view with all employees.
The panelDashboard component of the Oracle Application Development Framework (Oracle ADF) Faces feature is a JavaServer Faces (JSF) layout container that Oracle ADF developers can use to implement such information dashboards.
I decided to use it in a project that we are currently working on for the implementation of a complete school management solution using Oracle ADF 12c, but all cases that I found online used a static number of panelboxes. That was a problem for me because I needed a dynamic dashboard which would display a varying number of items, based on the records of a database table. At the same time the dashboard needed to be responsive, meaning that it should refresh after user “Create” and “Delete” operations.
The solution to this problem is the following:
The Model:
The model contains Employees entity object, EmployeesVO view object and HRAppModule application module based on HR schema
The view:
A page was created and a dashboard was dropped from the component palette.
Inside the paneldashboard an iterator was added.
Inside the iterator a panelbox was added and firstname, lastname attributes were dropped from data controls.
A button was also added to the panelbox’s toolbar to implement the Delete action.
The structure of the page is the following:
A tree binding was additionally created for the Employees, holding the EmployeesVO view object. The page definition looks like:
The properties of af:iterator need to be like the following:
The values of input texts need to be like:
The Panel dashboard was surrounded with a panelbox and a ‘Create’ button was added in the panelbox’ s toolbar
A popup was added in order to create an employee
A popup was added in order to confirm the deletion of an employee
PanelDashboard properties “columns=5” “Row height=100px”
The full code of the page can be found below:
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE html> <f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich"> <af:document title="dashboard.jsf" id="d1"> <af:messages id="m1"/> <af:form id="f1"> <af:panelBox text="PanelBox1" id="pb1"> <f:facet name="toolbar"> <af:toolbar id="t1"> <af:button text="Create" id="b1" actionListener="#{pageFlowScope.managedBean1.createEmployee}"/> </af:toolbar> </f:facet> <af:panelDashboard id="pd1" columns="5" rowHeight="100px" binding="#{pageFlowScope.managedBean1.employeeDashboard}"> <af:iterator id="i1" value="#{bindings.EmployeesVO1.collectionModel}" var="iter" rows="-1"> <af:panelBox id="pb2"> <f:facet name="toolbar"> <af:button text="Delete" id="b2" actionListener="#{pageFlowScope.managedBean1.showDeletePopup}"/> </f:facet> <af:panelFormLayout id="pfl1"> <af:inputText value="#{iter.bindings.LastName.inputValue}" label="#{bindings.LastName.hints.label}" required="#{bindings.LastName.hints.mandatory}" readOnly="true" columns="#{bindings.LastName.hints.displayWidth}" maximumLength="#{bindings.LastName.hints.precision}" shortDesc="#{bindings.LastName.hints.tooltip}" id="it1"> <f:validator binding="#{bindings.LastName.validator}"/> </af:inputText> <af:inputText value="#{iter.bindings.FirstName.inputValue}" label="#{bindings.FirstName.hints.label}" required="#{bindings.FirstName.hints.mandatory}" readOnly="true" columns="#{bindings.FirstName.hints.displayWidth}" maximumLength="#{bindings.FirstName.hints.precision}" shortDesc="#{bindings.FirstName.hints.tooltip}" id="it2"> <f:validator binding="#{bindings.FirstName.validator}"/> </af:inputText> </af:panelFormLayout> </af:panelBox> </af:iterator> </af:panelDashboard> <af:popup childCreation="deferred" autoCancel="disabled" id="p1" binding="#{pageFlowScope.managedBean1.employeePopup}" popupCanceledListener="#{pageFlowScope.managedBean1.employeePopupCanceledListener}"> <af:dialog id="d2" type="cancel"> <f:facet name="buttonBar"> <af:button text="OK" id="b3" actionListener="#{pageFlowScope.managedBean1.commitAndCloseEmployeePopup}"/> </f:facet> <af:panelFormLayout id="pfl2"> <af:inputText value="#{bindings.EmployeeId.inputValue}" label="#{bindings.EmployeeId.hints.label}" required="#{bindings.EmployeeId.hints.mandatory}" columns="#{bindings.EmployeeId.hints.displayWidth}" maximumLength="#{bindings.EmployeeId.hints.precision}" shortDesc="#{bindings.EmployeeId.hints.tooltip}" id="it12"> <f:validator binding="#{bindings.EmployeeId.validator}"/> <af:convertNumber groupingUsed="false" pattern="#{bindings.EmployeeId.format}"/> </af:inputText> <af:inputText value="#{bindings.FirstName.inputValue}" label="#{bindings.FirstName.hints.label}" required="#{bindings.FirstName.hints.mandatory}" columns="#{bindings.FirstName.hints.displayWidth}" maximumLength="#{bindings.FirstName.hints.precision}" shortDesc="#{bindings.FirstName.hints.tooltip}" id="it3"> <f:validator binding="#{bindings.FirstName.validator}"/> </af:inputText> <af:inputText value="#{bindings.LastName.inputValue}" label="#{bindings.LastName.hints.label}" required="#{bindings.LastName.hints.mandatory}" columns="#{bindings.LastName.hints.displayWidth}" maximumLength="#{bindings.LastName.hints.precision}" shortDesc="#{bindings.LastName.hints.tooltip}" id="it4"> <f:validator binding="#{bindings.LastName.validator}"/> </af:inputText> <af:inputText value="#{bindings.CommissionPct.inputValue}" label="#{bindings.CommissionPct.hints.label}" required="#{bindings.CommissionPct.hints.mandatory}" columns="#{bindings.CommissionPct.hints.displayWidth}" maximumLength="#{bindings.CommissionPct.hints.precision}" shortDesc="#{bindings.CommissionPct.hints.tooltip}" id="it5"> <f:validator binding="#{bindings.CommissionPct.validator}"/> <af:convertNumber groupingUsed="false" pattern="#{bindings.CommissionPct.format}"/> </af:inputText> <af:inputText value="#{bindings.DepartmentId.inputValue}" label="#{bindings.DepartmentId.hints.label}" required="#{bindings.DepartmentId.hints.mandatory}" columns="#{bindings.DepartmentId.hints.displayWidth}" maximumLength="#{bindings.DepartmentId.hints.precision}" shortDesc="#{bindings.DepartmentId.hints.tooltip}" id="it6"> <f:validator binding="#{bindings.DepartmentId.validator}"/> <af:convertNumber groupingUsed="false" pattern="#{bindings.DepartmentId.format}"/> </af:inputText> <af:inputText value="#{bindings.Email.inputValue}" label="#{bindings.Email.hints.label}" required="#{bindings.Email.hints.mandatory}" columns="#{bindings.Email.hints.displayWidth}" maximumLength="#{bindings.Email.hints.precision}" shortDesc="#{bindings.Email.hints.tooltip}" id="it7"> <f:validator binding="#{bindings.Email.validator}"/> </af:inputText> <af:inputDate value="#{bindings.HireDate.inputValue}" label="#{bindings.HireDate.hints.label}" required="#{bindings.HireDate.hints.mandatory}" columns="#{bindings.HireDate.hints.displayWidth}" shortDesc="#{bindings.HireDate.hints.tooltip}" id="id1"> <f:validator binding="#{bindings.HireDate.validator}"/> <af:convertDateTime pattern="#{bindings.HireDate.format}"/> </af:inputDate> <af:inputText value="#{bindings.IsActive.inputValue}" label="#{bindings.IsActive.hints.label}" required="#{bindings.IsActive.hints.mandatory}" columns="#{bindings.IsActive.hints.displayWidth}" maximumLength="#{bindings.IsActive.hints.precision}" shortDesc="#{bindings.IsActive.hints.tooltip}" id="it8"> <f:validator binding="#{bindings.IsActive.validator}"/> </af:inputText> <af:inputText value="#{bindings.JobId.inputValue}" label="#{bindings.JobId.hints.label}" required="#{bindings.JobId.hints.mandatory}" columns="#{bindings.JobId.hints.displayWidth}" maximumLength="#{bindings.JobId.hints.precision}" shortDesc="#{bindings.JobId.hints.tooltip}" id="it9"> <f:validator binding="#{bindings.JobId.validator}"/> </af:inputText> <af:inputText value="#{bindings.PhoneNumber.inputValue}" label="#{bindings.PhoneNumber.hints.label}" required="#{bindings.PhoneNumber.hints.mandatory}" columns="#{bindings.PhoneNumber.hints.displayWidth}" maximumLength="#{bindings.PhoneNumber.hints.precision}" shortDesc="#{bindings.PhoneNumber.hints.tooltip}" id="it10"> <f:validator binding="#{bindings.PhoneNumber.validator}"/> </af:inputText> <af:inputText value="#{bindings.Salary.inputValue}" label="#{bindings.Salary.hints.label}" required="#{bindings.Salary.hints.mandatory}" columns="#{bindings.Salary.hints.displayWidth}" maximumLength="#{bindings.Salary.hints.precision}" shortDesc="#{bindings.Salary.hints.tooltip}" id="it11"> <f:validator binding="#{bindings.Salary.validator}"/> <af:convertNumber groupingUsed="false" pattern="#{bindings.Salary.format}"/> </af:inputText> </af:panelFormLayout> </af:dialog> </af:popup> <af:popup childCreation="deferred" autoCancel="disabled" id="p2" binding="#{pageFlowScope.managedBean1.deleteEmployeePopup}"> <af:dialog id="d3" type="cancel"> <f:facet name="buttonBar"> <af:button text="OK" id="b4" actionListener="#{pageFlowScope.managedBean1.deleteEmployee}"/> </f:facet> <af:outputText value="Do you want to delete #{bindings.FirstName.inputValue} #{bindings.LastName.inputValue} ?" id="ot1"/> </af:dialog> </af:popup> </af:panelBox> </af:form> </af:document> </f:view>
The Backing Bean Implements the following methods:
- Action listener for delete button to set the iterator current row and show delete popup
- Action listener for create button to create and insert a new row
- Action listener for OK buttons to commit and close popups
- Popup canceled listener for employee popup for rollback and close popup
The Commit, Rollback and CreateInsert Operations were added to the bindings of the page.
Following is the full code of the backing bean:
package view; import javax.faces.event.ActionEvent; import oracle.adf.model.binding.DCIteratorBinding; import oracle.adf.view.rich.component.rich.RichPopup; import oracle.adf.view.rich.component.rich.layout.RichPanelDashboard; import oracle.adf.view.rich.context.AdfFacesContext; import oracle.adf.view.rich.event.PopupCanceledEvent; import oracle.jbo.Key; import oracle.jbo.Row; import oracle.jbo.RowSetIterator; public class DashboardBean { private RichPopup employeePopup; private RichPanelDashboard employeeDashboard; private RichPopup deleteEmployeePopup; public DashboardBean() { super(); } public void showDeletePopup(ActionEvent actionEvent) { Number employeeId = (Number) JSFUtils.resolveExpression("#{iter.bindings.EmployeeId.inputValue}"); RowSetIterator employeeIter = ADFUtils.findIterator("EmployeesVO1Iterator").getRowSetIterator(); Key lightKey = new Key (new Object[] {employeeId}); Row row = employeeIter.findByKey(lightKey, 1)[0]; employeeIter.setCurrentRow(row); RichPopup.PopupHints hints = new RichPopup.PopupHints(); deleteEmployeePopup.show(hints); } public void createEmployee(ActionEvent actionEvent) { ADFUtils.findOperation("CreateInsert").execute(); RichPopup.PopupHints hints = new RichPopup.PopupHints(); employeePopup.show(hints); } public void setEmployeePopup(RichPopup employeePopup) { this.employeePopup = employeePopup; } public RichPopup getEmployeePopup() { return employeePopup; } public void employeePopupCanceledListener(PopupCanceledEvent popupCanceledEvent) { ADFUtils.findOperation("Rollback").execute(); employeePopup.hide(); AdfFacesContext.getCurrentInstance().addPartialTarget(employeeDashboard); } public void commitAndCloseEmployeePopup(ActionEvent actionEvent) { ADFUtils.findOperation("Commit").execute(); employeePopup.hide(); AdfFacesContext.getCurrentInstance().addPartialTarget(employeeDashboard); } public void setEmployeeDashboard(RichPanelDashboard employeeDashboard) { this.employeeDashboard = employeeDashboard; } public RichPanelDashboard getEmployeeDashboard() { return employeeDashboard; } public void deleteEmployee(ActionEvent actionEvent) { ADFUtils.findOperation("Delete").execute(); ADFUtils.findOperation("Commit").execute(); deleteEmployeePopup.hide(); AdfFacesContext.getCurrentInstance().addPartialTarget(employeeDashboard); } public void setDeleteEmployeePopup(RichPopup deleteEmployeePopup) { this.deleteEmployeePopup = deleteEmployeePopup; } public RichPopup getDeleteEmployeePopup() { return deleteEmployeePopup; } }
The resulting layout can be seen in the following screeshots:
- Dynamic Dashboard - August 31, 2015