In my previous post I created a tree binding definition with a correspondent iterator binding dynamically at runtime, and mentioned that this technique can be used to build a declarative component browsing data of any view object passed to the component as an attribute. In this post I'm going to show how we can do that.
The source code of our declarative component looks like this:
Let's have a look at DynTableBean's code:
And finally, let's use our declarative component. We're going to put on a page two tables browsing departments and employees:
That's it! The sample application for this post is available here. I used JDeveloper 11.1.2.3.0.
The source code of our declarative component looks like this:
<af:componentDef componentVar="dyntable" var="attrs">The component has attributes for ViewObject name, VO's row set iterator name and data control name. Have a note that we use a read-only dynamic table approach. Table's columns are going to be rendered dynamically by the forEach tag according to the VO's attributes and their UI hints.
<af:xmlContent> <component xmlns="http://xmlns.oracle.com/adf/faces/rich/component"> <attribute> <attribute-name>ViewObjectName</attribute-name> <required>true</required> </attribute> <attribute> <attribute-name>RSIName</attribute-name> </attribute> <attribute> <attribute-name>DataControlName</attribute-name> <required>true</required> </attribute> </component> </af:xmlContent> <af:table rows="#{backingBeanScope.DynTableBean.tree.rangeSize}" fetchSize="#{backingBeanScope.DynTableBean.tree.rangeSize}" emptyText="#{backingBeanScope.DynTableBean.tree.viewable ? 'No data to display.' : 'Access Denied.'}" var="row" rowBandingInterval="0" value="#{backingBeanScope.DynTableBean.tree.collectionModel}" id="dc_t1"> <af:forEach items="#{backingBeanScope.DynTableBean.tree.attributeDefs}" var="def"> <af:column headerText="#{backingBeanScope.DynTableBean.tree.labels[def.name]}" sortable="true" sortProperty="#{def.name}" id="dc_c1"> <af:outputText value="#{row[def.name]}" id="dc_ot1"/> </af:column> </af:forEach> </af:table> </af:componentDef>
Let's have a look at DynTableBean's code:
private static final String ITERATOR_NAME = "Iterator"; private static final String TREE_NAME = "TREE_"; public DCIteratorBinding getIterator(String iteratorName, String voName, String rsiName) { DCBindingContainer dcb = getBindings(); DCIteratorBinding jub = dcb.findIteratorBinding(iteratorName); if (jub == null) { //Create and init an iterator binding definition JUIteratorDef jid = new JUIteratorDef(iteratorName, null, voName, rsiName, 25); HashMap initValues = new HashMap(); initValues.put(JUTags.DataControl, getDataControlName()); jid.init(initValues); //Create an iterator binding instance jub = jid.createIterBinding(BindingContext.getCurrent(), dcb); //Add the instance to the current binding container dcb.addIteratorBinding(iteratorName, jub); } return jub; } public JUCtrlHierBinding getTree() { DCBindingContainer dcb = getBindings(); //May be the VEmp tree binding is already created JUCtrlHierBinding chb = (JUCtrlHierBinding)dcb.findCtrlBinding(getTreeName()); if (chb == null) { // if not //Looking for the VEmpIterator iterator JUIteratorBinding iter = (JUIteratorBinding)getIterator(getIteratorName(), getVOName(), getRSIName()); //Create and init a tree binding definition JUCtrlHierDef hierDef = new FacesCtrlHierDef(); HashMap initValues = new HashMap(); initValues.put(JUCtrlHierDef.PNAME_IterBinding, iter.getName()); JUCtrlHierTypeBinding typeBinding = new JUCtrlHierTypeBinding(); initValues.put(JUCtrlHierDef.PNAME_TypeBindings, new JUCtrlHierTypeBinding[] { typeBinding }); hierDef.init(initValues); //Create a tree binding instance chb = (JUCtrlHierBinding)hierDef.createControlBinding(dcb); //Add the instance to the current binding container dcb.addControlBinding(getTreeName(), chb); } return chb; } //Generate a name of the tree bindning as "TREE_" + component's Id //For example: TREE_dc1, TREE_dc2, ... private String getTreeName() { return new StringBuilder(TREE_NAME).append(getID()).toString(); } //Generate a name of the iterator bindning as "Iterator_" + component's Id //For example: Iterator_dc1, Iterator_dc2, ... private String getIteratorName() { return new StringBuilder(ITERATOR_NAME ).append(getID()).toString(); } //Getters for the component's attributes - // ViewObjectName, RSIName, DataControlName, id private String getVOName() { return (String) getMyself().getAttributes().get("ViewObjectName"); } private String getRSIName() { return (String) getMyself().getAttributes().get("RSIName"); } private String getDataControlName() { return (String) getMyself().getAttributes().get("DataControlName"); } private String getID() { return (String) getMyself().getAttributes().get("id"); } //A couple of helper methods to get an instance of our component private RichDynamicDeclarativeComponent getMyself() { RichDynamicDeclarativeComponent _this = (RichDynamicDeclarativeComponent)getValueObject("#{dyntable}", RichDynamicDeclarativeComponent.class); return _this; } private static Object getValueObject(String expr, Class returnType) { FacesContext fc = FacesContext.getCurrentInstance(); ELContext elctx = fc.getELContext(); ExpressionFactory elFactory = fc.getApplication().getExpressionFactory(); ValueExpression valueExpr = elFactory.createValueExpression(elctx, expr, returnType); return valueExpr.getValue(elctx); }
And finally, let's use our declarative component. We're going to put on a page two tables browsing departments and employees:
<af:form id="f1"> <!--Browsing departments view object VDept --> <af:declarativeComponent viewId="DynTable.jsff" id="dc1" DataControlName="AppModuleDataControl" ViewObjectName="VDept" /> <!--Browsing employees view object VEmp --> <af:declarativeComponent viewId="DynTable.jsff" id="dc2" DataControlName="AppModuleDataControl" ViewObjectName="VEmp" /> </af:form>
Thanks Eugene for the wonderful post.
ReplyDeleteYou are awesome as always !!!