We have a requirement to have an editable master-detail information in which the master should be an editable form and the detail should be an editable table. This is a usual user-interface requirement for a accounting application especially on journal voucher entry screens (believe me here because I am a Certified Public Accountant :) ). In my encounter, the challenge was on the editable detail table. As much as possible I am sticking to the drag and drop from Data Controls panel style of creating the UI, but I cannot stick to it this time especially that this detail table has dependency on the master form (like "What's the applicable exchange rate defined in the master form?"- which is to be used by the rows of the detail table to translate an original currency Debits and Credits to base currency amounts) plus the fact that I need to conditionally set the visibility and required attribute of other components in the detail-table row based on the property of a selected account (an object on the same row).
Below is an illustrative code fragment to demonstrate the idea.
<af:panelCollection id="pc1">
<f:facet name="toolbar">
<af:toolbar id="t3">
<af:commandToolbarButton text="Add"
id="ctb2"
actionListener="#{myBackingBean.addNewMyDetailObject}"/>
<af:commandToolbarButton text="Remove"
id="ctb3"
actionListener="#{myBackingBean.removeSelectedMyDetailObject}"
immediate="true" />
</af:toolbar>
</f:facet>
<af:table value="#{myBackingBean.myMasterObject.myDetailObjectList}"
var="row" id="t2"
partialTriggers="::ctb2 ::ctb3"
binding="#{myBackingBean.myDetailObjectRichTable}"
rowSelection="single"
<af:column sortable="false" headerText="Property 1" id="c1"
<af:inputText value="#{row.myDetailObjectProperty1}" id="it1"/>
</af:column>
<af:column sortable="false" headerText="Property 2" id="c2"
<af:inputText value="#{row.myDetailObjectProperty2}" id="it2"/>
</af:column>
</af:table>
</af:panelCollection>
I made a dummy backing bean class below to show the sample methods to insert and remove a row from the editable detail table:
package blogspot.soadev.view.backing;
import java.io.Serializable;
import java.util.List;
import javax.faces.event.ActionEvent;
import oracle.adf.view.rich.component.rich.data.RichTable;
public class MyBackingBean implements Serializable {
private MyMasterObject myMasterObject;
private RichTable myDetailObjectRichTable;
public void addNewMyDetailObject(ActionEvent e) {
MyDetailObject myDetailObject = new MyDetailObject();
// add my deatil object to master
myMasterObject.addMyDetailObject(myDetailObject);
//can initialize myDetailObject properties if needed
myDetailObject.setMyDetailObjectProperty1("property1 can be complex type");
myDetailObject.setMyDetailObjectProperty2("property2 can be complex type");
}
public void removeSelectedMyDetailObject(ActionEvent e) {
//get the selected row data
MyDetailObject myDetailObject = (MyDetailObject)myDetailObjectRichTable.getSelectedRowData();
//remove it from the master object
myMasterObject.removeMyDetailObject(myDetailObject);
//this my tip - refreshes the table stamping.
//this is especilly needed if you conditionally set the attributes
//of other components (like visibility, rendered, required, etc)
//if you will not invoke this, you will have inconsistent table row state.
myDetailObjectRichTable.resetStampState();
}
public void setMyDetailObjectRichTable(RichTable myDetailObjectRichTable) {
this.myDetailObjectRichTable = myDetailObjectRichTable;
}
public RichTable getMyDetailObjectRichTable() {
return myDetailObjectRichTable;
}
public void setMyMasterObject(MyMasterObject myMasterObject) {
this.myMasterObject = myMasterObject;
}
public MyMasterObject getMyMasterObject() {
return myMasterObject;
}
}
class MyDetailObject{
private MyMasterObject myMasterObject;
private Object myDetailObjectProperty1;
private Object myDetailObjectProperty2;
public void setMyMasterObject(MyMasterObject myMasterObject) {
this.myMasterObject = myMasterObject;
}
public MyMasterObject getMyMasterObject() {
return myMasterObject;
}
public void setMyDetailObjectProperty2(Object myDetailObjectProperty2) {
this.myDetailObjectProperty2 = myDetailObjectProperty2;
}
public Object getMyDetailObjectProperty2() {
return myDetailObjectProperty2;
}
public void setMyDetailObjectProperty1(Object myDetailObjectProperty1) {
this.myDetailObjectProperty1 = myDetailObjectProperty1;
}
public Object getMyDetailObjectProperty1() {
return myDetailObjectProperty1;
}
}
class MyMasterObject{
private List<MyDetailObject> myDetailObjectList;
public void addMyDetailObject(MyDetailObject myDetailObject){
getMyDetailObjectList().add(myDetailObject);
myDetailObject.setMyMasterObject(this);
}
public void removeMyDetailObject(MyDetailObject myDetailObject){
getMyDetailObjectList().remove(myDetailObject);
myDetailObject.setMyMasterObject(null);
}
public void setMyDetailObjectList(List<MyDetailObject> myDetailObjectList) {
this.myDetailObjectList = myDetailObjectList;
}
public List<MyDetailObject> getMyDetailObjectList() {
if(myDetailObjectList == null){
myDetailObjectList = new ArrayList<MyDetailObject>();
return myDetailObjectList;
}
}
Please note of my tip on the table resetStampState() method above.You maybe asking by now- "Where is the EJB part?", well, my master object is bound to an EJB DataControl ;) .
