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 ;) .
am getting a null pointer exception on
ReplyDeletemyMasterObject.addMyDetailObject(myDetailObject);
how am I supposed to bind myMasterObject or initialize it
Hi Peter,
ReplyDeleteAs I have noted above this was just an illustrative code, nevertheless, I have modified to code to fix some unescaped characters which were not rendered and updated the "getMyDetailObjectList" method to resolve the NullPointerException.
regards,
Pino
Am still getting the same error at the same place.
ReplyDeleteI think there should be some binding on myMasterObject
Hi Peter,
ReplyDeleteIn my case, I have something like the following:
public MyMasterObject getMyMasterObject(){
if(myMasterObject == null){
myMasterObject = (MyMasterObject)getCurrentRowDataProvider("findMyMasterObjectByIdIterator");
}
return myMasterObject;
}
notes:
->For the "getCurrentRowDataProvider", method refer to the following post: http://soadev.blogspot.com/2010/03/adf-model-how-to-programmatically.html
->It is assumed that you are using data control for the master object.
regards,
Pino
If I will have enough time this weekend, I will update this post with a sample project for your convenience.
ReplyDelete