Adding sortable columns to a table in a component

From Joomla! Documentation

Revision as of 07:27, 28 May 2013 by Wilsonge (talk | contribs) (→‎See also: Removing 1.5 link)
Quill icon.png
Content is Incomplete

This article or section is incomplete, which means it may be lacking information. You are welcome to assist in its completion by editing it as well. If this article or section has not been edited in several days, please consider helping complete the content.
This article was last edited by Wilsonge (talk| contribs) 10 years ago. (Purge)

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 required and details you need to be aware of so 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!

Step 1: The Model[edit]

Note: Since Joomla 2.5 the populateState of JModelList does pagination and ordering via it's own populateState() and you can skip this part. If you need other states than the parent provides just do a parent::populateState(); in your specific one (and make sure "filter_order" is in your filter_fields config)

The first thing is to add the filter_order and filter_order_Dir request populateState() method of your model. Change 'default_column_name' to the name of the column you want to use as the default sort, and change the filter order direction if you wish Adding this information to the State object here insures it's accessible to all of the code that might need it.

public function populateState() {
	$filter_order = JRequest::getCmd('filter_order');
	$filter_order_Dir = JRequest::getCmd('filter_order_Dir');
	
	$this->setState('filter_order', $filter_order);
	$this->setState('filter_order_Dir', $filter_order_Dir);
}


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 HTML table. Most often this is the getListQuery() method, but might not be.

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.

This information gets called by whatever function builds the ORDER BY clause, typically a private function like this:

public function getListQuery() {
	$db = JFactory::getDbo();
	$query = $db->getQuery(true);
	
	// ...
	
	$query->order($db->getEscaped($this->getState('filter_order', 'default_sort_column')) . ' ' . $db->getEscaped($this->getState('filter_order_Dir', 'ASC')));
	
	return $query;
}

Replace 'default_sort_column' with the column you want to sort by by default. You can also change 'ASC' to 'DESC', depending on which way you want to sort by default.

Step 2: The View[edit]

Having generated the sorting variables in the model, you need to assign them to the view so that they show up on the page when it is displayed.

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

public function display($tpl=null) {
	$items = $this->get('Items');
	$state = $this->get('State');
	
	$this->sortDirection = $state->get('filter_order_Dir');
	$this->sortColumn = $state->get('filter_order');
	
	parent::display($tpl);
}

Step 3: The Template[edit]

Now 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 sort parameters added. 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->sortColumn; ?>" />
<input type="hidden" name="filter_order_Dir" value="<?php echo $this->sortDirection; ?>" />
</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', 'DbNameColumn', $this->sortDirection, $this->sortColumn); ?></th>
	<th><?php echo JHTML::_( 'grid.sort', 'Description', 'DbDescriptionColumn', $this->sortDirection, $this->sortColumn); ?></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 4: Styling the result[edit]

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[edit]