Data, but not Database Data

Some customers I met last week said that even though automatic databinding is handy (this is when rowsets dropped on visual components are automatically bound to these components), they don't want a tight coupling to the database in this way. They have their own middle tier, and they want to use it.

How can they use the Data Table component, with its paging features etc., and display arbitrary data as opposed to data fetched automagically from a databound rowset?

It's actually pretty straightforward. There is just one key concept you need to know: the JSF Data Table interacts with its data through the DataModel interface. You can supply your own data model, and the Data Table will happily display your own data.

When you drop a Data Table component in Creator, you automatically get a Data Model associated with the Data Table created. For dataTable1 you get dataTable1Model. This model is an instanceof javax.faces.model.ListDataModel, which wraps a simple java.util.List. Each row in the data table will come from a single item in this list.

How the row object is accessed depends on what you put in the table columns. You may have noticed that when you drop a database table on the Data Table, you by default get Output Text components with value bindings like these:

value="#{currentRow['FIRSTNAME']}
This lets each column in the table logically bind to different columns of the row object. To achieve the same effect, put a Map in each row of the data model, where the keys are keys to be used in the value binding expressions (like "FIRSTNAME" above), and the values are the actual values you want assigned as the value for the bound output texts.

Let's get specific. You've dropped a JSF Data Table component on your canvas, and you ended up with dataTable1, and its data model, dataTable1Model. Go to your page bean (double click on the canvas somewhere outside the Data Table), and modify the end of the constructor to

  1. Create a List object
  2. Populate the List with Hashmaps, one for each row, where each hashmap contains key/value pairs for all columns in this row
  3. When done, set this list as the wrapped data for the data model
  4. Return to the Data Table in the design view, and modify the value bindings for the columns such that they point to the new keys

For example, your code might look something like this:

        (Creator-managed Component Initialization here)
        // Additional user provided initialization code
        List rows = new ArrayList();

        // You would probably have a loop here to fetch your
        // data from your real storage or middle tier
        HashMap row1 = new HashMap();
        row1.put("FIRSTNAME", "Mickey");
        row1.put("LASTNAME", "Mouse");
        row1.put("FAVCOLOR", "Green");

        HashMap row2 = new HashMap();
        row2.put("FIRSTNAME", "Donald");
        row2.put("LASTNAME", "Duck");
        row2.put("FAVCOLOR", "Blue");

        rows.add(row1);
        rows.add(row2);

        dataTable2Model.setWrappedData(rows);
    }

Now return to the Design view, right click on the Data Table, bring up the Table Layout customizer. Get rid of all the existing columns that are there (using the << button), then use the New button to create your own columns. In my example, we'll want three columns. Choose the heading names, like "First", "Last", and "Favorite Color". Choose the component types - for example, Output Texts. And finally, set the value binding expressions. For example, for the first column, you should set the Value to #{currentRow['FIRSTNAME']}.

Voila! When you run you should see your own data displayed in the data table. If you enable Paging in the Table Layout customizer, that should work too.

Using ArrayList and HashMap (and one for every row at that) may strike you as wildly inefficient. But notice how you're supplying the List and Map objects! You don't have to use the default implementations; you can write your own class implementing java.util.List which maps directly into your own data storage for the data to be displayed, and similarly, the Map lookup from column name to column value can be done through smart computation rather than pre-storing all the row values in a series of HashMaps for the rows!

One final note - if the data being displayed can change as the page is displayed repeatedly (for example due to failed validation or because the user is interacting with other components on the page), you probably don't want to construct your wrapped list data in the constructor. Instead, make the List object a class member, such as

    private ArrayList rows;
and add a method which first calls rows.clear() and then updates the list to contain the current correct set of rows. Then call this update method both from the constructor as well as from beforeRenderResponse() (which is a method you should add to your page bean; its parent class has an empty implementation which is called.)

This (the need to call the update method from both places) is a bit ugly and is something we'll fix. Happy coding!

Comments:

This worked qhite well for me, even tho' I didn't implement the entire HashMap interface. I use it to enable a dataTable to display the current "order in process" bean (order details) without having to commit them to the db. (I do that at the end of the multi-page ordering process). Very Handy, thanks Tor. cheers, Kristofer

Posted by Kristofer Younger on February 25, 2005 at 12:12 AM PST #

hi Tor and thanks for this article BUT do it may that dynamically adding row to table data, for example into action button method. or even delete from table data into another action button

Posted by Majid Keshtidar on April 15, 2005 at 06:32 AM PDT #

Sorry, parse error :-) Can you restate that question?

Posted by Tor Norbye on April 15, 2005 at 06:38 AM PDT #

Tor, I implemented this exactly as you described and it works perfectly. Now I do not know much about the DataTable component, can you comment on what I would need to add to your solution in order to select a row?

Posted by Jacob Pays on August 05, 2005 at 07:12 AM PDT #

I'm not sure what you mean exactly by "select a row", but if you mean how to visually highlight the row, there are lots of data table ways to do that. I've blogged on some of that before. Take a look at this entry for example: http://blogs.sun.com/roller/page/tor?entry=creator_how_to_highlight_specific

Posted by Tor Norbye on August 06, 2005 at 03:07 PM PDT #

Actually I meant get the selected row in code. To your example I have added a checkbox as the first column. I then have a Search button on the Page. In the searchButton_action method I have added calls to dataTable1.getRowIndex() dataTable1Model.getRowIndex() but these always return -1 I need the actual rowIndex. I don't know what I'm missing?

Posted by Jacob Pays on August 07, 2005 at 11:47 PM PDT #

Hi Tor, Related to this topic,i have a question. In the data table, i am displaying the information which are present in my database. I have to give an option to the user in order to select a particular row and edit the contents of that row. how to acheive this functionality? Already i have a Row Selection(Check box) column which is bounded to the primary key of my database. I am really puzzled what i have to write in my Edit button action method which will make the user to edit the contents of a selected row by clicking the check box. Please let me know the reply ASAP.

Posted by prasanna karthik.S on January 01, 2006 at 06:10 PM PST #

Tor, thanks for the post. I am working with Creator 2 and have tried to follow your directions, but I have noticed that the table I added to the screen did not put a dataTableModel. It does add a DefaultTableDataProvider but I don't see any setWraper method. Is there now a new way to do this? I, like a few other people use another middleware (hibernate) and need to use a table. Thanks

Posted by Steve Michael on June 13, 2006 at 05:37 AM PDT #

I have a datatable in which data is displayed as follows List rows = new ArrayList(); HashMap row1 = new HashMap(); row1.put("FIRSTNAME", "Mickey"); row1.put("LASTNAME", "Mouse"); row1.put("FAVCOLOR", "Green"); rows.add(row1); dataTable2Model.setWrappedData(rows); whenever i fetch data from the table it works fine when the number of rows in the data table is 4.But whenever row count is more than 4 the content from the 5th row fetched will be null. And also dataTable1Model RowCount property is 4 and it cannot be changed in the properties window. Is there any solution to fetch more than 4 rows?

Posted by manikanta on July 24, 2006 at 02:16 PM PDT #

I work with Creator2 and I would like to populate rows of a datatable with a collection of pojo. On your blog, I found an article about "Data, but not Database Data" http://blogs.sun.com/roller/page/tor?entry=data_but_not_database_data but I think it works well with datatable component of Creator1 only. How to fill a such table with a collection of bean for example ?

Posted by regis on August 03, 2006 at 10:41 PM PDT #

I work with Creator2 and I would like to populate rows of a datatable with a collection of pojo. On your blog, I found an article about "Data, but not Database Data" http://blogs.sun.com/roller/page/tor?entry=data_but_not_database_data but I think it works well with datatable component of Creator1 only. How to fill a such table with a collection of bean for example ?

Posted by regis on August 03, 2006 at 10:42 PM PDT #

In order to display data from Pojo's you could use the ObjectListDataProvider. My blog http://blogs.sun.com/roller/page/winston?entry=objectlistdataprovider_workaround has an example project on how to do this. If you want to display just two dimensional array of primitive type data, you can also do it. Please read my blog http://blogs.sun.com/roller/page/winston?entry=displaying_two_dimensional_data on this.

Posted by Winston Prakash on August 04, 2006 at 01:16 AM PDT #

Tor, thanx for the post. My question is this: Since I display my data in the dataTable by iterating over a list, how can I edit the fields in the datatable and bind the values to my List (in the backing bean)? Whenever I add a button that does a submit() on the form, all original and modified data in the datatable is dropped by the refresh.... I would like to show the datatable with it's original content in input fields, permit changes and catch theses changes in the backing bean in order to save. Is this possible?

Posted by JS on August 08, 2006 at 05:43 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

Tor Norbye

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today