A very small article on a very trivial issue. I have a page with a Master-Detail construction with a (master) Table of Departments and a (detail) Table of Employees. My challenge here: how can I display the name of the currently selected Department in the heading for the Employees table? Using ADF Bindings, this should be dead easy? And indeed: it is.
The steps:
1. First identify the iterator used for the Departments table:
<af:table value="#{bindings.DeptView1.collectionModel}" var="row"
2. Then create the reference to the current row in this iterator and the required attribute in that row:
... text="Employees in the selected Department #{bindings.DeptView1.currentRow.dname}"
3. However, the Row that is returned here is the generic ViewRowImpl class. That class has no getDname() method, only a generic getAttribute() method.
The most direct (though least generic) solution here is to generate the specific DeptViewRowImpl class, by checking the appropriate checkbox on the ViewObject wizard:
Alternatively, we can create our own AMISViewRowImpl that has getMap() method that returns an object that implements the Map interface and turns any call like view.map[‘attributeName’] or view.map.attributeName (these two are equivalent for Maps) into the appropriate getAttribute(‘attributeName’) call on the underlying Row instance.
Implementing a Generic RowReader
The alternative approach, that does not require us to generate the ViewRowImpl for every ViewObject, is using a generic RowReader. A Class that accepts a Row as input and returns the value of the requested attribute. I have quickly created such a generic set up, that allows declarative expressions like this one:
... text="Employees in the selected Department #{RowReader[bindings.DeptView1.currentRow].Dname}"
to access any attribute value on any row object. The Row object is passed into the RowReader managed bean and then the desired attribute is indicated. Note that for attributes of type Number we have to specify the value property for proper display.
The required configuration for this generic RowReader is the following managed bean entry in the faces-config.xml file:
<managed-bean>
<managed-bean-name>RowReader</managed-bean-name>
<managed-bean-class>nl.amis.adf.model.RowReaderHandler</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
And of course the RowReaderHandler and RowReader classes themselves:
package nl.amis.adf.model;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import oracle.jbo.Row;
public class RowReaderHandler implements Map {
public RowReaderHandler() {
}
public Object get(Object key) {
RowReader rr = new RowReader();
rr.setRow((Row)key);
return rr;
}
... remainder of Map implementation}
package nl.amis.adf.model;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import oracle.jbo.Row;
public class RowReader implements Map {
private Row row;
public RowReader() {
}
/**
* @param key The Name of the Attribute whose value we want to see (key should be a String)
* @return The value of the attribute specified through the key in the row set through setRow()
*/
public Object get(Object key) {
return row.getAttribute((String)key);
}
public void setRow(Row row) {
this.row = row;
}
public Row getRow() {
return row;
}
... remainder of default Map implementation
}
Note how the RowReaderHandler implements the Map interface, get’s setup with the Row instance – through the call from the EL expression to the get method – and then instantiates the real RowHandler, that also happens to implement the Map interface. The RowHandler is ‘injected’ with the Row. Any call on that RowReader object will read the requested value from the Row.
Luca,
CanI have th source code for the Radio buttons and the ajaxing on the master detail
Â
Thanks
Sagar
Hi,
I used the above soln “#bindings.EmployeeView1.currentRow.employeeId}†and was able to access the value.
My doubt is that how “employeeId” got resolved to “EmployeeId”. ie, “E” changed to “e”
Its very confusing to follow it. Is there convetion used how EL expression resolves the names.
Regards
Shiva
Asim solution works great and very simple!
#{bindings.DeptView1.currentRow.dataProvider.dname}
thanks
trivial issues have trivial solutions 🙂
text=”Employees in the selected Department #{bindings.DeptView1.currentRow.dname}” <– Wrong: as it is rowImpl
text=”Employees in the selected Department #{bindings.DeptView1.currentRow.dataProvider.dname}” <– works like charm
If “Dname” is an attribute binding in the current page’s binding container bound to the “Dname” attribute of the DeptViewIterator iterator binding, then you should also just be able to use the EL expression “#{bindings.Dname.inputValue}” to reference the value of the Dname of the current row in that iterator. In your example, you won’t already have that attribute value binding in your page def if you only have two ADF Faces tables (since they both use Table/Range bindings). You can create the attribute value binding by selecting the “bindings” folder in the Structure Window of the page definition and using “Insert Inside bindings > attributeValues” right mouse menu pick.