Faking Sortable View Data
Sun Jul 22 15:28:00 EDT 2012
There's a point in most decent-sized XPages apps I write where I switch from using standard data sources like xp:dominoView
to writing Java classes that implement List
, usually due to some multi-source data merging I have to do that would be extremely cumbersome or slow to do otherwise. This works well, but one problem is that view data sources have special powers that plain-Jane List
s lack, such as easy sortable column headers.
Having encountered TabularDataModel
a bit ago when making an activity stream out of several databases' views, I decided to see how easy it would be to take a fairly standard set of Java data objects - say, a List
of Map
s, which is conceptually similar to a non-categorized view - and put it into a xp:viewPanel
and have all sortable columns. In short: weird, but overall easy.
The TabularDataModel
abstract class is very easy to implement: you only need to implement methods for the total number of rows and one to get the current row's data (it uses an internal index accessible via .getRowIndex()
). On its own, that's not interesting, since it doesn't get you anything a normal List
wouldn't. They key is when you override a couple more methods:
.isColumnSortable(String columnName)
: thexp:viewPanel
calls this with a column programmatic name to determine whether it's sortable at all - I made mine return true in all cases..getResortType(String columnName)
: once the panel knows a column is sortable, it checks to see what sorting directions it supports. In my case, I return 3, indicating it's sortable both ascending and descending..getResortState(String columnName)
: returns the current sorted state of the specified column. Presumably, the column name would be useful if you did custom cascading sorting, which I didn't for now - I just have it return the current sort direction of whatever column is sorted..getResortColumn()
: returns the name of the currently-sorted column..setResortOrder(String columnName, String sortOrder)
: this is the interesting one, where the view panel tells the model that it wants to sort a specified column in a specified direction (or "toggle").
When the resort method is called, my class sorts its internal List
object by the given column/key name in the given direction via a pretty simple comparator. Once it's all put together, you have the data from the view in a standard format and sortable by all columns.
As usual, there are caveats. First and foremost, there's a reason why you can't normally sort by just any column in a Domino view: Notes was made in 1989 and has barely been improved since then sorting large amounts of data arbitrarily is an expensive operation. It's only practical in this sort of situation because the amount of data is pretty small (only 200 entries) and then initial loading is relatively quick. If you had hundreds of thousands of entries in the view, this would be painfully slow to load and rather greedy with server memory. Additionally, I chose to punt on multi-value columns: I just turn anything that isn't directly comparable (like Vector
s) into String
s. Nonetheless, this kind of thing may be practical on its own and is definitely a good exercise in extending your data-manipulation capabilities.
For development, I created a quick database with a couple documents with somewhat randomized data: a date/time value offset by a random amount of hours or days, a random number from -512 to 512, and the document's UNID and Note ID, listed in a view with no sortable columns. The class I wrote just scrapes through the view data with a ViewNavigator
and puts each of the entries into a Map
. The demo page shows two standard xp:viewPanel
s (one showing the view directly, the other showing my SortableMapView
object) and, on the second tab, the Java code for the three objects I used.