Using an af:iterator to group and insert breaks
8 April 2008 at 12:35 CEST | In Features and tips, JDeveloper, Oracle |Yesterday I got a question how to create a ADF Faces JSPX page that shows all the records from a ViewObject, but inserts group headers when one of the fields (category) in the ViewObject changes. In the end, the solution lies in the use of an <af:iterator> component to iterate the rows in the ViewObject and insert breaks where necessary.
The end result looks something like this:

Read the rest of this blog to learn how I achieved this.
First, let me show the output of the SQL statement of the ViewObject:
-- ---------------------------------------- ---- ---------------------
7 Business Intelligence Enterprise Edition N Business Intelligence
6 Businesss Intelligence Standard Edition Y Business Intelligence
8 Data Integrator N Business Intelligence
1 Database Enterprise Edition Y Database Products
2 Database Standard Edition N Database Products
3 Oracle Lite N Database Products
4 Application Server Enterprise Edition N Fusion Middleware
5 Forms and Reports N Fusion Middleware
I've create a default ADF Business Components Entity Object, View Object, and Application Module. To use a checkbox I need a boolean type attribute, so I added a transient attribute to the View Object:
Next thing to do is to create the ADF Faces JSPX page to iterate over all the rows in the ViewObject and include a checkbox component for each row. Whenever the category of a row changes compared to the previous row a header should be included to indicate the start of a new category. We normally use an af:table component to show multi-record stuff, but you can't use it in this case. It does not allow to output the headers in between the rows. Hence we use the af:iterator component to iterate over the rows of the ViewObject:
value="#{bindings.TestWilfredViewIterator.allRowsInRange}">
<af:panelHeader text="Category #{row.category}"
rendered="#{CategoriesBean.changed}"/>
<af:selectBooleanCheckbox text="#{row.text}"
value="#{row.trueBool}"/>
</af:iterator>
The af:iterator component iterators over a binding called TestWilfredViewIterator. For each iteration it creates a variable "row" that can be used in the child components of the af:iterator.
The first child component is an af:panelheader to show a header for each new category. It's text property uses an EL expression #{row.category} to get the category text from the current row. The rendered property uses an EL expression to call out to a managed bean. That bean contains the logic if the header should be shown or not. It determines this by comparing the category with the category of the previous row. If it changed, the header should be printed otherwise not.
The next child component is an af:selectbooleancheckbox to include a checkbox input element. It's text ("label") uses an EL expression to get the description from the current row. The value is bound to the TrueBool transient attribute we added earlier. The setter of that transient attribute sets the value of the Bool attribute to either "Y" or "N".
The page definition file contains the binding declarations:
<iterator id="TestWilfredViewIterator" Binds="TestWilfredView"
RangeSize="-1" DataControl="AppModuleDataControl"/>
</executables>
It declares a iterator with the ID of "TestWilfredViewIterator". It binds to the ADF BC ViewObject "TestWilfredView" from the data control named "AppModuleDataControl". The RangeSize is set to -1 so all rows are queried and no pagination is used.
The backing bean contains the code to determine if the header should be printed:
throw new IllegalStateException("do not call setChanged");
}
public boolean isChanged() {
// get the key of the current row
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
Object key =
application.createValueBinding("#{row.key}").getValue(context);
// compare this key with the key of the previous call (bean can be
// called multiple times for same row)
if (!key.equals(previousKey)) {
// new key, compare categories
Object category =
application.createValueBinding("#{row.category}").getValue(context);
changed = !category.equals(previousCategory);
// remember category for next call
previousCategory = category;
}
// remember key for next call
previousKey = key;
return changed;
}
The managed bean needs both a setter and a getter for the boolean property. We don't really need the setter, but it has to be there to be able to reference the bean property from an EL expression. Without the setter, it is not a bean property and you cannot reference it from EL.
It turns out that this method is sometimes called multiple times for each row of the af:iterator component. This requires some additional handling in the java code. You cannot just assume that the next call of the method also represents the next row of the af:iterator.
So, the code first gets the key of the current row of the af:iterator component. The key is compared to the key from the previous cal to isChanged(). If the key has changed (or previousKey is null), we know we have found the next row of the iterator. We then continue to get the category of the current row. We compare that to the category of the previous call to isChanged(). If it is different, we set a boolean property to indicate this row has a different category than the previous row. We need to store this in a property to make sure that subsequent calls to isChanged() for the same ViewObject row return the same result as the first call for that particular row.
That's all there's to it. Just one last thing, to make the story complete. The managed bean declaration in the faces-config.xml:
<managed-bean-name>CategoriesBean</managed-bean-name>
<managed-bean-class>com.example.sandbox.view.beans.Categories</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
9 Comments
Sorry, the comment form is closed at this time.
Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds.


Hi,
My row.variableName doesn't show anything when I write similar code to your example. I created an ADF Business Components Entity Object, View Object, and Application Module. I made a pagedef file and added an iterator. The code uses the 'bindings' variable. I don't get an error but I don't see where that variable is created. Can you give me any advice to get this working? Thanks in Advance. Dave Gress
Comment by david Gress — 1 July 2008 #
I added Attribute bindinds form the columns in the table. I can get one value to show by using bindings.Username. When I use row.Username I get 500 Internal Server Error javax.faces.el.PropertyNotFoundException: Error getting property 'Username' from bean of type oracle.jbo.server.ViewRowImpl.
Obviously the row variable is not being recognized. How did you solve this issue? TIA, Dave
Comment by david Gress — 1 July 2008 #
Unbelievable! I had the variable written as 'Username' and changed it to 'username' and now the code is working.
Thanks for the example.
Comment by david Gress — 2 July 2008 #
Oops. Sorry I missed your comment and didn't reply. Good to hear you solved it yourself.
Comment by Wilfred van der Deijl — 16 July 2008 #
Good to hear you solved it yourself.
Comment by izmir temizlik firmaları — 19 June 2009 #
Hi,
I am trying to use this inside the iterator, but it gives an error:
Error:
varname is an unknown property.
How do I solve this?
Thanks
Comment by sai — 29 July 2009 #
Hi,
I am trying to use this inside the iterator, but it gives an error:
[code][/code]
Error:
varname is an unknown property.
How do I solve this?
Thanks
Comment by Sai — 29 July 2009 #
thanks for information.. its really good..
Comment by izmir temizlik firmaları — 14 September 2009 #
Hi,
Even for us the var name is not being recognised. Any help would be appreciated.
Regards
Radhika
Comment by radhika — 7 October 2009 #