Reputation: 2427
I finally reached the point in my web app where implementing simple requirements is becoming overwhelming. I'm guessing it's time for a nice refactoring or a simple garbage and redo.
Here are the simple requirements I need to implement on a table:
1) make the columns sortable 2) freeze the 2 header rows and first 3 columns
Now let me explain why it got so complicated. The table isn't a simple 2 dimensions table. The first 3 columns are name, email and role after that there is an undefined amount of columns representing 3 types of data per day. In other words the user will define how many days of data he wants and each day is going to be represented by 3 columns that we'll call data1, data2 and data3.
An unskilled ASCII representation would be like that:
01/01/1970 01/02/1970 total
name email role data1 data2 data3 data1 data2 data3 data1 data2 data3
toto [email protected] boss 23 test 54 10 test1 54
This is a very poor representation but that should give you a vague idea of what the output should look like. So I need to display the dates in the first row, but 3 columns offset, then the headers where the 3 first headers are fixed but the following columns are repetitions of the same 3 headers, one for each day and finally I need to put all the data in the right place....
Right now I'm getting this to display correctly (no sorting and no freeze though) using a JSP and a custom data structure coming from the controller that I iterate through in different ways to display all the elements.
Now you probably start to understand my headache... with so many things custom made and maybe not necessarily done the proper / efficient way how would I go to add those new features? Sorting and Freezing?
I did look at some jquery datagrids and other javascript components but none of them seemed flexible enough to allow for 2 rows of headers with the 2nd row grouping based on the first row.
Any idea?
Thanks.
Upvotes: 1
Views: 296
Reputation: 6729
The best UI framework i've seen so far is ExtJs. You can visit: http://extjs.com/deploy/dev/examples/grid/array-grid.html to see their grid in action. It's extremely easy to use and support.
Upvotes: 0
Reputation: 819
This is a very custom GUI, so I'd use a fairly home-grown solution. I'd break down the problem into a few steps and make the browser responsible for most of the work (ergo JavaScript).
First, you want object to represent the data. Based on the problem, I'd suggest you have a three-tier object structure: one to represent the user (each row in the example); a child array in this object would hold each day's data; and each member of the array would be an object that holds the cells the user chose. JSP would be responsible for writing out this data structure, but JavaScript would take it from there. Here's what the constructors would look like:
function DayOfData(dateString, dataObject) {
this.date = Date.parse(dateString); //storing a Date is cleaner separation of presentation & data
this.dataCells = dataObject;
}
function User(name, email, role) {
this.name = name; //and so on
this.data = [];
this.addData = function(date, dataSet) {
this.data.push( new DayOfData(date, dataObject) );
}
}
The instantiations as written by JSP (or whatever is in the middle tier) would look like this:
var users = []; //a global to hold it all
users[someId] = new User("foo", "[email protected]", "boss");
users[someId].addData("1/1/1970", {"label 1": 23, "label 2": "test"} );
//...and so on
Notice the JSON object that stores the data cells that the user chose to display. In this anonymous object, I'm correlating the visual label with the value; this is simpler than creating yet another child object to abstract the label from some unique ID. You could go that extra step if it was really necessary, but it's one more level in the hierarchy, so I think it's best to go the simpler route.
(Of course, you'd want to namespace all this properly... I'm using naked global variables to simplify the syntax. For a really robust system, I'd use private members as well.)
I would use TrimPath in the display phase (after JSP has written out the data in this structure). Simply pass the users array to a template that would look something like this (this would get placed in a table by the display code after TrimPath processes it):
<tbody>
{for user in user}
<tr>
<td>${user.name}</td> <!-- and so on -->
{for day in user.data}
{for datum in day}
<td>${datum}</td>
{/for}
{/for}
</tr>
{for}
</tbody>
You'd need to do a slight variation on this pattern to write the header row, though you probably could do that easier in the server-side code, since it's not a dynamic part of the GUI. To get the label row to "freeze" at the top of the table (it will be in a THEAD element), just use CSS to set the TBODY's height, then set overflow-y to "scroll". That ought to achieve the effect you want.
To re-sort the columns, you'd actually re-sort the users array (see descriptions of Array.sort) based on the column the user clicks. Then, clear out the TBODY and reprocess the template with TrimPath.
This sounds complicated and involved, but it's really not. You want to avoid making it difficult to change the layout, so separating data and presentation is key. A collection of data-only JavaScript objects coupled with TrimPath (or similar) templates as outlined above accomplishes a good MVC pattern, and makes the problem simpler by breaking it down into discrete steps. Managing a sortable table of data (not to mention data that is grouped horizontally into major divisions) -- that would be next to impossible without good MVC.
Upvotes: 2