views:

200

answers:

2

Hi everyone,

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.

+2  A: 

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.

pluckyglen
A: 

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.

ifesdjeen