A Simple Goal
In the dashboard there are two types of rows: the project details and the build status. For 1.5 we had removed the build status row and instead attached it as a tool-tip. However, there were a number of complaints about this – people didn’t like the way we (read I) implemented the tool tips.
So, after a quick trip back to the design board, I decided to put the build status back in as a static row. However this is where the problem starts – the project grid in 1.5 was changed to use client-side sorting (save on the round-trips to the server.) Just adding the build status gives some weird results:
Notice that the build status says “Setup for documentation generation” and the project that is actually building is the CCNet-Doc project. The build status line should be associated with the CCNet-Doc project, not the CCNet project! If multiple projects are building, it will put all the build statuses at the bottom of the grid (or the top if the grid is sorted in reverse order!)
So, how can this be fixed?
Some Background
Before I move onto how I resolved the problem, first some background.
Since this is client-side functionality, the actual sorting is done via JavaScript. Rather than re-inventing the wheel, we use jQuery and the tableSorter plug-in by Christian Bach (http://tablesorter.com/docs/index.html). This provides the basic functionality for sorting with a small footprint.
However this plug-in does not have the ability to “lock” rows together. In Christian’s defence, I’m not sure how it could be done in a generic way.
But, not all is lost. As well as providing the basic functionality the tableSorter plug-in also exposes an extension system to allow people to manipulate the grid after it has been sorted. This is what I used to “lock” the rows together.
The HTML
In order to make sense of the code below, here is an example of the HTML used to generate the above table:
<table class="SortableGrid" id="StatusGrid">
<thead>
<tr class="ProjectGridHeader">
<!-- Header definition here -->
</tr>
</thead>
<tbody>
<tr id="projectData1">
<!-- Project details definition here: CCNet -->
</tr>
<tr id="projectData2">
<!-- Project details definition here: CCNet-Doc -->
</tr>
<tr class="buildStatus" id="link2">
<!-- Build status definition here: CCNet-Doc -->
</tr>
</tbody>
<tfoot>
<tr>
<!-- Footer definition here -->
</tr>
</tfoot>
</table>
I have omitted the details of each row – for the code below it is not required.
The important parts to note are:
- Each <tr> in the <tbody> element has an id tag.
- For project details rows the id starts with “projectData”
- For build status rows the id starts with “link”
- For linked rows the number in the id is the same (e.g. 2 in the example above)
- All the build status rows have a class of “buildStatus”
This is all generated by the nVelocity template on the server.
Some jQuery Goodness
Now to lock the rows together, all we need is a bit of jQuery and some knowledge on the tableSorter plug-in.
To add an extension to the plug-in is a simple matter of adding a widget. This is done at a global level and then associated with the table when the tableSorter is initialised.
Here is the widget that I added:
$.tablesorter.addWidget({
id: 'statusDisplay',
format:function(table){
$("tr.buildStatus",table.tBodies[0]).each(function(){
var row = $(this);
// Get the identifier of the owning row
var id = row.attr('id');
var linkedId = 'projectData' + id.substring(4);
var parent = $('#' + linkedId);
// Move the row to after the owning row
row.insertAfter(parent);
});
}
});
This selects gets all the rows with the “buildStatus” class and iterates through each row doing the following:
- gets the id of the row
- calculates the id of the linked row
- gets the linked row
- moves the status row underneath the details row (insertAfter will move the row)
All fairly simple with jQuery.
The final part is to associate the widget with the actual tableSorter instance. This is done when the tableSorter is initialised. I used the extensibility model of jQuery so I could separate the code from the template:
$.fn.initialiseProjectGrid = function(config){
// Initialise the configuration
var defaultConfig = {
sortList: [[0,0]]
};
config = $.extend(defaultConfig, config || {});
// Initialise the sorting
this.tablesorter({
sortList: config.sortList,
widgets: ['statusDisplay']
});
// Allow chaining
return this;
};
This ensures that the configuration is set, then initialises the tableSorter and associates the widget. To actually use the function is as simple as:
$('#StatusGrid').initialiseProjectGrid();
And that’s all there is to it.
In Summary
Here’s what the grid now looks like:
The status row is now underneath the project details where it should be. Sorting on the different columns will correctly keep the status row under the project it is for.
This also show how easy it is to extend the tableSorter plug-in to add some extra functionality. Thanks to Christian for making it so easy
