API plug-in development

The DataTable's API provides a large number of methods that can be used to manipulate the table and gather data from it. Many of the methods that you will need are built-in, but you might also find it useful to be able to create a plug-in API method to extend the built-in functionality. This page discusses how to create a plug-in API method for DataTables.

Before going into detail of how to create plug-in methods, it might be useful to consider what sort of plug-ins might be created:

  • Short cuts for common actions using the standard API (shorten multiple calls into a single method)
  • Data summary methods (sum, average, standard variation, etc)
  • Table manipulation for feature plug-ins.

The DataTables API

Before creating a new API method for DataTables it is important to understand the fundamentals of the API, particularly the concepts of chaining where methods return their own instance so other methods can immediately be called on the result of the first method, and the result set.

The DataTables API instance is array like in that it looks very much like a Javascript array and has many of the same methods and properties (.length for example) - the content of that array is the "result set". It is worth noting that API is not technically an array, just very like it, much in the same way that jQuery is array like where you can access nodes using array syntax.

The result set of an instance can be an empty array, an array of row, column or cell indexes, data objects or anything else. All types of data will be encountered in the result set and what is contained will depend very much upon the nature of your own methods.

Execution scope

Plug-in API methods are executed in the scope of that API instance - for instance this.rows() will call the rows() method. As such the full set of the built in API is available to your own methods, in addition to whatever Javascript manipulation of data and the DOM you wish to perform.

In addition to this, the table's that the API instance should operate on are held in the this.context parameter. The context parameter is an array of DataTables settings objects (which are unique for each table) - it has to be an array as the API is multi-table aware and the method should operate on all tables refereed to by context.

Registering a new method

A new API method can be registered using the DataTable.Api.register() static method. It takes two parameters and has no return:

  • Input parameters:
    • Method name
    • Method function

The method name is the full chained expression). For example, to add a rows().sum() method you would use that full string (rows().sum()). The sum() method would only be available to the result of the rows() method - which will be added by the DataTables API processor automatically. Consider also sum() as a "top level method" which would be available to any API instance, regardless of its chain.

The method function is the function that describes the logic of the plug-in. As noted above it is executed in the scope of the API instance. Additionally it can return any value (including the instance for chaining if no other return value is expected) and will accept whatever parameters you require for your method.

Examples

Let us consider two examples to illustrate these points. The first, sum() will be a top level method that will simply sum the data in the result set and return it. The second will attach to the rows() method and provide the ability to add a class to the selected rows. It will return the instance for chaining.

sum()

The sum() method is actually very simple - we loop over the data in the result set and simple add it all together and return the value. The end result is that we have a method that can be used to sum numeric data, when the result set has numeric data in it - for example from a call to columns().data().

DataTable.Api.register( 'sum()', function () {
    var sum = 0;

    for ( var i=0, ien=this.length ; i<ien ; i++ ) {
        sum += this[i];
    }

    return sum;
} );

Example calls:

  • table.column( 2 ).data().sum();
  • `table.cells( '.selected', 3 ).data().sum();

rows().addClass()

This example is more complex as we need to take in to account the multi-table nature of the API. What needs to happen is that we loop over each context in the API (i.e. each table) and for each also loop over the rows in that table and add the class where required.

This is the code in the fullest form:

DataTable.Api.register( 'rows().addClass()', function ( klass ) {
    var context = this.context;

    for ( var i=0, ien=this.length ; i<ien ; i++ ) {
        var innerApi = new DataTable.Api( context[i] );

        for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
            var node = innerApi.row( this[i][j] ).node();
            $(node).addClass( klass );
        }
    }

    return this;
} );

Note that rows() will populate the result set with an array of arrays! One inner array for each table and each inner array contains the indexes for that table. In the code above, this means that this.context.length === this.length (i.e. there is one item in the result set for each table).

As looping over multiple tables in this form is common in the API, DataTables provides an iterator method to simplify it. iterator will handle the double for loop internally (and number of other loop types) and provide information about the loop to a closure function. The above code can thus be simplified to be:

DataTable.Api.register( 'rows().addClass()', function ( klass ) {
    return this.iterator( 'row', function ( context, index ) {
        var node = new DataTable.Api( context ).row( index ).node();
        $(node).addClass( klass );
    } );
} );

As of DataTables 1.10.3 this can be simplified further as the callback methods for iterator are executed in the scope of an API instance that contains only the table in question in its context:

DataTable.Api.register( 'rows().addClass()', function ( klass ) {
    return this.iterator( 'row', function ( context, index ) {
        var node = this.row( index ).node();
        $(node).addClass( klass );
    } );
} );