Actions

J1.5

Adding sortable columns to a table in a component

From Joomla! Documentation

Revision as of 17:02, 6 June 2009 by Chris Davenport (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Replacement filing cabinet.png
This Namespace has been archived - Please Do Not Edit or Create Pages in this namespace. Pages contain information for a Joomla! version which is no longer supported. It exists only as a historical reference, will not be improved and its content may be incomplete.

Given that you have a table of data already in your component, how do you make some, or all, of the table columns sortable, like many of them are in the Joomla Administrator? It's not particularly hard to do, but there are several steps and you need to be aware of the details so that everything fits together properly. There are variations on the procedure given here and once you are confident that you understand how it all works you should feel free to explore other possibilities that may suit your purposes better.

This procedure assumes that your component is structured according to the Model-View-Controller (MVC) design pattern. The general idea behind the procedure will still be applicable to non-MVC components if you apply a bit of imagination!

Contents

Step 1: The component layout file

Starting with the user interface, you need to add some elements to the component layout file. The table must be included in a form. This might already be the case as, for example, you might already have implemented pagination or filtering on the table. But if the table is not yet in a form then now is the time to wrap it in <form> and </form> tags. The reason that a form is required is that the sortable columns rely on a bit of JavaScript that will submit the form with fresh parameters to obtain a new table. Naturally, this will involve a page load, so if you would prefer an AJAX-based solution, then this procedure is not for you.

The form tags will look something like this:

<form id="adminForm" action="<?php echo JRoute::_( 'index.php' );?>" method="post" name="adminForm">
 
.... table goes here ....
 
</form>

Notice that the form name must be "adminForm". You may need to adjust the action URL in some cases.

You also need to add a couple of hidden fields to the form. They can be placed anywhere between the <form> and </form> tags, but generally they are placed just before the closing tag, like this:

<form id="adminForm" action="<?php echo JRoute::_( 'index.php' );?>" method="post" name="adminForm">
 
.... table goes here ....
 
<input type="hidden" name="filter_order" value="<?php echo $this->lists['order']; ?>" />
<input type="hidden" name="filter_order_Dir" value="" />
</form>

Now look at the table itself. You might have a table with static headings already, looking vaguely like this:

<tr>
        <th>Name</th>
        <th>Description</th>
</tr>

You need to replace the static column names with calls to the Joomla JHTML static class, so that your code will look something like this:

<tr>
        <th><?php echo JHTML::_( 'grid.sort', 'Name', 'DbName', $this->lists['order_Dir'], $this->lists['order']); ?></th>
        <th><?php echo JHTML::_( 'grid.sort', 'Description', 'DbDescription', $this->lists['order_Dir'], $this->lists['order']); ?></th>
</tr>

You will definitely need to adapt this code to your specific requirements. The arguments to the JHTML call are as follows:

  1. Must be 'grid.sort' so that JHTML will insert the correct behaviour for a sortable column.
  2. This is the name of the column that your visitors will actually see. You need to change this for your particular table columns.
  3. This is the name of the corresponding database field (column) that is to be sorted on. This will be passed to the model, most likely so it can be added to an "ORDER BY" clause in the SQL query statement.
  4. Must be exactly as shown here. It is the current order direction (ascending or descending) and comes from the view (see later).
  5. Must be exactly as shown here. It is the name of the column that the table is currently sorted on and comes from the view (see later).

In short, you need to amend the second and third arguments to each JHTML call appropriately.

Finally, if your sortable table is going to be in the front-end of your site, then you need to add a little snippet of JavaScript to the layout. Alternatively, you can add it to the view code (using JDocument->addScriptDeclaration) if you would rather keep your JavaScript code in the HTML <head> section.

<script language="javascript" type="text/javascript">
function tableOrdering( order, dir, task )
{
        var form = document.adminForm;
 
        form.filter_order.value = order;
        form.filter_order_Dir.value = dir;
        document.adminForm.submit( task );
}
</script>

You don't need to add this code if your sortable table is in the Administrator as this code is loaded for you automatically anyway.

This completes the changes you need to make to the layout. JHTML grid.sort will now add a call to the tableOrdering function so that tableOrdering will be called whenever the user clicks on the column header. tableOrdering puts the name of the column that was clicked, and the sort direction, into the hidden form fields and submits the form. In the next step you will see how the model picks up those field values and amends the database query appropriately.

Step 2: The model

In the model which generates the data that will form the table, you need to make a change to the method which builds the database query that will be used to populate the table. Most often this is the getData() method, but might not be; check in the view to see which method is actually being used (see later).

The model could pull data in from anywhere; it doesn't have to be a database, but in the vast majority of cases the model will be using the Joomla database API to submit SQL queries to a database. Assuming that to be the case, you need to adjust the query so that the sort parameters are taken into account.

The following is typical of a private method used to build the ORDER BY clause:

function _buildContentOrderBy()
{
        global $mainframe, $option;
 
        $filter_order     = $mainframe->getUserStateFromRequest( $option.'filter_order', 'filter_order', 'DbOrder', 'cmd' );
        $filter_order_Dir = $mainframe->getUserStateFromRequest( $option.'filter_order_Dir', 'filter_order_Dir', 'asc', 'word' );
 
        $orderby = ' ORDER BY '.$filter_order.' '.$filter_order_Dir.';
 
        return $orderby;
}

You will need to adjust the "DbOrder" to match the name of the database column to sort on by default. If you want the default ordering to be descending order then change the "asc" to "desc" in the $filter_order_Dir statement.

Step 3: The view

Having generated the data in the model, using the sort parameters that were passed using the form which wraps the table in the layout, you now need to make sure that these same sort parameters are passed back into the layout so that when the page loads with freshly sorted data, the correct defaults for the sort column and sort direction are shown to the user.

To do this you need to add a few lines of code to your view file, view.html.php:

$state =& $this->get( 'state' );
 
// Table ordering.
$lists['order_Dir'] = $state->get( 'filter_order_dir' );
$lists['order']     = $state->get( 'filter_order' );
 
$this->assignRef( 'lists', $lists );

There shouldn't be any need to customise this code.

You should now test to make sure it all works as it should.

Step 4: Styling the result

Finally. you might want to apply a bit of CSS styling to make the output a bit more attractive.

Selecting the sortable columns can only be done via their context, so you will probably need to add a CSS class to the <table>, the <tr> or to the <th> tags. This is what the output might look like with a class added to the tag:

<tr class="sortable">
        <th><a href="javascript:tableOrdering('DbName','asc','');" title="Click to sort by this column">Training provider</a></th>
        <th><a href="javascript:tableOrdering('DbDescription','asc','');" title="Click to sort by this column">Location</a></th>
</tr>

To add a bit of space between the column name and the ascending/descending indicator image (a common requirement), you could then apply CSS like this:

tr.sortable th img {
        margin-left: 5px;
}

See also