Using & Understanding Datatables    Datatable

When it comes to displaying a large amount of data then the Datatable class is the right one. It allows us to render data from the database in tabular manner and offers functions like sorting, paging, fulltext search, etc. All that with a simple SQL query. Thats the first and most important fact here to remember: The Datatable can only display data from a SQL query (SELECT * FROM ...).

Okay, this article will explain the most important things you should know when working with the Datatable. All the examples here will connect to a sample database whose connectionstring is stored in a constant called DATA_CONNSTRING. So you will see a connection call to a different database than the default on. It should not confuse you because in your environment you can use your default configured connection. Furthermore we will with only one table called person which contains the columns id, firstname and lastname.

First Simple Datatable


<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — datatable.asp


Voila, we've got our first naked Datatable!

You can see that we've included our Datatable class and created an instance of it called table. The instance has been defined globally because its necessary to access it during different steps of our page lifecycle. All your datatable settings must be done within the init() and your table must be drawn within the main() (to actually render the HTML at a given position) and within the callback() procedure (to process all requests its callback requests).
Last but not least you see that we have set the sql property which is being used to populate the Datatable. Thats all the stuff you require to hook up your first one. They are a must!

Defining columns

You may have recognized that the columns in our first example have been detected automatically and rendered within your browser (column names are made human readable as well check str.humanize()). Thats cool, but not always what we want. We might want to change order, hide some columns, etc. For that reason its possible to add the columns manually and as long as there is at least one column added manually the automatic detection is deactivated. newColumn() method is our friend in that case. Check that:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  with table
    .sql = "SELECT id, firstname, lastname, lastname + ', ' + firstname AS name FROM person"
    .sort = "lastname, firstname"
    .newColumn "name", "Name"
    .newColumn "lastname", "Last"
    .newColumn "firstname", "First"
  end with
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — columns.asp


We've added 3 columns manually using newColumn() within our init() procedure. The first parameter is the name of the column as it appears in the SQL Query and the second parameter is caption which should be displayed as the column headline.

Pro TIP: The second parameter can also be an array which would result that the first field is the caption and the second a tooltip. E.g.
.newColumn "firstname", array("First", "Users Firstname")

Yeah, we also selected a column named name (which concatenates firstname and lastname) within the SQL query. Thus we were also able to add it as a column to the Datatable.

Primary key columns: The first column is always being considered as the primary key column (which uniquely identifies each record). If you are missing that then you get an exception which tells you to set pkColumn property manually to the column which contains the primary key.

By the way you can see that the sort property has been used to set the initial sorting of the data. You cannot use an ORDER BY clause within your SQL Query. Thats being added by the component itself. The sort property can contain any valid ORDER BY expressions though. Examples:


'valid sorting expressions
table.sort = "firstname DESC, lastname"
table.sort = "id, name, lastname"


Setting column properties:
If you check the API docs you will see that the newColumn methods returns an instance of a DatatableColumn. Thats great, because we can grab the column and customize it even more. Check the following example where we assign a css class to the id column to make it look gray in order to not attract too much attention. In addition we also styled the lastname column.

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  
  'add id column and set CSS class
  'quickes way (but only one property)
  table.newColumn("id", "ID").cssClass = "light"
  
  'add lastname column and set CSS class
  'another way of assigning properties
  with table.newColumn("lastname", "Name")
    .cssClass = "name"
    .help = "Some tooltip for the header"
  end with
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>
  
  <style>
    .light {
      color:#bbb;
    }
    .name {
      color:#f00;
      text-align:center;
      font-weight:bold;
    }
  </style>
  <% table.draw() %> 

<% end sub %>
Run this code — columnProperty.asp


The power is within the SQL


The power of our Datatable is "hidden" within the SQL query. As you may have already noted its only possible to use columns which are defined within the SQL query. That shouldn't be seen as a negative constraint at all. Its good news for us instead. SQL is really powerful and thats why you can solve most of the problems directly within the SQL query already. Remember the custom column creation? It could be solved without adding columns manully as well. Just use an SQL query like this to achieve a renaming of columns (this should be familiar to most):

 
table.sql = "SELECT id, firstname AS first, lastname AS last FROM person"

' would result in a datable with the following columns:
' id | first | last


You can even use SQL dialect for simple if conditions (CASE). Use your imagination and you will be able to solve every problem. E.g. you could even chuck HTML code into your SQL like (though I would recommend to avoid it as much as possible - just because it lacks readability):

 
table.sql = "SELECT id, '<strong>' + firstname + '</strong>' AS first FROM person"

When using HTML within your data be sure to set encodeHTML to false on the respective column. Otherwise all HTML will be safely encoded by default.

Runtime modifications


What if you need to change values during runtime according to its values or according to some business logic you have written? Thats not a big deal and can be solved using events. There are 2 main events. You can hook up either a whole row and modify the properties of a row (DatatableRow) during runtime or you can do the same for a column. First should be used when you want to apply customizations on a per record basis and the latter is perfect when you want to deal with a column across all records. Lets take a deeper look at both of them.

onRowCreated Event
The onRowCreated event is fired when the current row instance has been created but just before its been rendered. It has to be set on the datatable instance. At this stage you can modify any properties of the current row instance which is being held in the row property of your Datatable instance (check the API docs of DatatableRow for all its members). The following example will assign a css class called inactive to all persons whose ID is less than 10. Makes not much "real world" sense but should do it :)

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  table.onRowCreated = "onRowHandler"
end sub

sub onRowHandler(dt)
  if clng(dt.data("id")) < 10 then
    dt.row.cssClass = "inactive"
  end if 
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <style>
    .inactive td {
      color:#ccc;
    }
  </style>

  <% table.draw() %> 

<% end sub %>
Run this code — onRow.asp


Right, onRowCreated is nothing more as a normal property. Just that the string it holds is the name of the procedure which will be called everytime a row has been created. The procedure has to accept one argument which is the Datatable instance itself. That makes it convenient to access all datatable properties during runtime and makes it even possible to reuse event handling procedures across more datatables.

You see we use that argument (dt in that case) and its row property to get access to our current row instance. Also very interesting is the usage of the data property which always gives us access to underlying recordset. That allows us to read all the records data during runtime. Powerful!

onCellCreated Event
The other way to control execution is by using the onCellCreated event of a Datatable column. Its fired when a given cell of that column has been created but not rendered yet. Thus you can change its properties according to any conditions you like. Lets check the following example which writes "less 10" if the ID is less than 10 and "greater 10" if the ID is greater instead of the ID itself. Additionally it should highlight all persons whose lastname starts with the letter "A".

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  
  'quick way of attaching an event handling procedure
  table.newColumn("id", "ID").onCellCreated = "onID"
  
  'other way...
  set c = table.newColumn("lastname", "Last")
  c.onCellCreated = "onLastname"
end sub

'Important: its a function! onRowCreated was a sub!
function onID(dt)
  onID = "less 10"
  dt.col.cssClass = "inactive"
  if dt.col > 10 then
    onID = "greater 10"
    dt.col.cssClass = ""
  end if
end function

function onLastname(dt)
  'important to pass through the original value
  'as we dont change it
  onLastname = dt.col.value
  'note; we can use dt.col instead of dt.col.value
  'as its the default property
  if str.startsWith(dt.col, "A") then
    dt.col.cssClass = "ePerson"
  else
    'important to clear it as it would remember
    'it for the whole column
    dt.col.cssClass = ""
  end if
end function

sub callback(action)
  table.draw()
end sub

sub main() %>

  <style>
    .inactive {
      color:#ccc;
    }
    .ePerson {
      color:#00f;
    }
  </style>

  <% table.draw() %> 

<% end sub %>
Run this code — onCell.asp


The process is the same as with onRowCreated apart from the fact that this time we need to define a function instead of a sub. Its because our cell event handlers need to return the actual value which will be rendered. The simplest handler would be just passing through the value:


<%
'same as if you would not use it. just pass through
function onFirstname(dt)
  onFirstname = dt.col.value
end function
%>


Action columns
For sure you want to add a column one day which allows you to perform some action on a record or you want to open a form for the modification of a record. There are many ways to solve that but most of them require the onCellCreated event.

You could e.g create link for a column:


<%
function onLastname(dt)
  onLastname = "<a href=""person.asp?id=" & dt.data("id")  & """>" & dt.col & "</a>"
end function
%>


or you could also create an empty column, hook up a cell created event and add action links to it. This example shows one which redirects to a page and another one which would call a javascript function (you can even use ajaxed.callback) to perform an action:


<%
function onAction(dt)
  onAction = "<a href=""person.asp?id=" & dt.data("id")  & """>edit</a>" & _
    "<a href="javascript:void(0)" onclick=""doDelete(" & dt.data("id") & ")"">delete</a>"
end function
%>


Styling your Datatable. Lets Pimp!


The whole Datatable control can be styled using pure CSS. So that everthing you need to know ;) By default a standard ajaxed theme is used if you do not specify a stylesheet explicitly using the css property (which takes a virtual path to a stylehseet file). If you specify it then yours is loaded instead of the standard one. Another option would be to just add your own stylesheet to the page which will override the standard settings. Thats not recommended though, as the standard appearance might change over time.

So what you have to figure out then is what css class names are being used and define them within you stylesheet. To figure out that just create a new Datatable and have a look at the source code (firebug recommended!). The class names won't change as they are part of the style contract. So lets create our first theme and apply it a Datatable.

td, th {
  padding: 0.2em 1em;
  font: 10pt arial;
  width: 200px;
}

.axdDTControlsRow {
  background:#bbb;
}

th, th * {
  background:#000;
  color:#fff;
}

.axdDTRowOdd td {
  background:#eee;
}

.axdDTHighlight {
  font-weight:bold;
  color:#f00;
}

.sortedASC, .sortedDESC {
  font-weight:bold;
}

tfoot tr {
  background:#bbb;
  color:#fff;
}

tbody tr:hover td {
  background:#B6E2F3;
}

.pagingBar span {
  padding:0.1em 0.3em;
}

.pagingBar .pCurrent {
  font-weight:bold;
  background:#000;
  color:#fff;
}
Run this code — style.css


I am not a Designer so its just for demonstration purpose ;) Last but not least we need the Datatable which uses our just created theme. The new part here is the reference to the theme using the css property.

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  table.css = "style.css"
  table.recsPerPage = 10
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — pimped.asp


Share your Theme: Please share your Datatable themes with others by posting to our Discussion Group. Its great to have some nice and good looking datatables around.

Now you should be able to create your data tables from the scratch and style them. You will see that after some days you get used to it and its great fun. Its a powerful component with great flexibility. More functions are coming...

Debugging: Check your log files if you want to debug the Datatable. You will find the whole generated SQL which is being sent to the database when populating the table.

Posted in Datatable 5601 days ago

Version 2.1.1 Released3964 days ago