DataTables: Custom Response Handling
Asked Answered
V

2

15

I started working on AngularJS and DataTables and wonder whether it is possible to customize the response DataTables is expecting. The current expectation of the DataTables plugin is something like this:

{
    "draw": 1,
    "recordsTotal": 57,
    "recordsFiltered": 5,
    "data": [...]
}

On the server end, the API's are being handled by django-tastypie

The response from server is:

{
     meta: {
        limit: 20,
        next: null,
        offset: 0,
        previous: null,
        total_count: 2
     },

     objects: [...]
 }

So, is there a way to tweak Datatables Plugin to accept/map this response, or I'll have to find a way to add expected fields to the api?

So far I've done this:

    var deptTable = angular.element('#deptManagementTable').DataTable({
        processing: true,
        serverSide: true,
        pagingType: "simple_numbers",
        ajax: {
            url: "/client/api/v1/departments/",
            data: function(d) {
                d.limit = d.length;
                d.offset = d.start;
                d.dept_name__icontains = d.search.value;
            },
            dataSrc: function(json) {
                for (var i=0, len=json.objects.length ; i<len ; i++) {
                    json.objects[i].DT_RowId = json.objects[i].dept_id;
                }
                return json.objects;
            }
        },
        aLengthMenu: [
            [5, 25, 50, 100],
            [5, 25, 50, 100]
        ],
        iDisplayLength: 5,
        columns: [
            {
                data: "dept_name"
            },
            {
                data: "dept_created_on",
                render: function ( data, type, full, meta ) {
                    var dateCreated = new Date(data);
                    dateCreated = dateCreated.toLocaleDateString();
                    return dateCreated;
                }
            }
        ]
    });

Any help will be appreciated.

Thanks in Advance :)

Vierra answered 8/8, 2014 at 20:12 Comment(0)
G
24

You can pass a function to the DataTables ajax option, this will give you full control over how to fetch and map the data before passing it to DataTables.

.DataTable({
    serverSide: true,
    ajax: function(data, callback, settings) {
        // make a regular ajax request using data.start and data.length
        $.get('/client/api/v1/departments/', {
            limit: data.length,
            offset: data.start,
            dept_name__icontains: data.search.value
        }, function(res) {
            // map your server's response to the DataTables format and pass it to
            // DataTables' callback
            callback({
                recordsTotal: res.meta.total_count,
                recordsFiltered: res.meta.total_count,
                data: res.objects
            });
        });
    }
});

The solution above has been tested with jQuery DataTables 1.10.4.


As this question is tagged with Angular, here's a solution for those using angular-datatables.

<div ng-controller="testCtrl">
    <table datatable dt-options="dtOptions" dt-columns="dtColumns" class="row-border hover"></table>
</div>

.controller('testCtrl', function($scope, $http, DTOptionsBuilder, DTColumnBuilder) {
    $scope.dtOptions = DTOptionsBuilder.newOptions()
        .withOption('serverSide', true)
        .withOption('ajax', function(data, callback, settings) {
            // make an ajax request using data.start and data.length
            $http.get('/client/api/v1/departments/', {
                limit: data.length,
                offset: data.start,
                dept_name__icontains: data.search.value
            }).success(function(res) {
                // map your server's response to the DataTables format and pass it to
                // DataTables' callback
                callback({
                    recordsTotal: res.meta.total_count,
                    recordsFiltered: res.meta.total_count,
                    data: res.objects
                });
            });
        })
        .withDataProp('data'); // IMPORTANT¹

    $scope.dtColumns = [
        // your column definitions here
    ];
});

The solution above has been tested with angular-datatables 0.3.0 + DataTables 1.10.4.

¹ The important part to note here is that the angular-datatables solution requires .withDataProp('data') to work, while the pure jQuery DataTables solution does not have a data: 'data' option.

Guertin answered 4/12, 2014 at 15:0 Comment(4)
I found this question from Google without any answers, and I struggled for a couple hours to solve the same problem, so here's a solution for future readers.Medrano
i haven't tested the code, but seems like a neat solution to me, so accepting the answer. a big thanks for sharing the solution.Vierra
Thanks. I've first coded this solution using angular-datatables, and translated it to the regular jQuery DataTables plugin which your question is making use of. I've then tested it and fixed some nits. I can update the answer to add the angular-datatables solution as well.Medrano
Awesome solution as this allows the datatables requests to be bound to $http interceptors which otherwise would need a non-angular workaround and extra issues.Marishamariska
S
2

This answer was very useful, but seems a bit outdated in the context of the current (1.10.12 at the moment) version of datatables, which actually makes life a lot easier (or at least more readable).

Under the current version you can do something like the following in your declaration (bearing in mind that tastypie needs to have the filterable & ordering options set for the fields you want to use).

You can now access the data being submitted in the ajax request by doing data.attr inside the function.

This assumes you wish to restrict searching to one field, but can easily be extended in the same manner do console.log(data) within the ajax function to see what is sent.

var table = $('#tableName').DataTable({
    "deferRender":true,
    "serverSide": true,
    "ajax": function(data, callback, settings) {
        var sort_column_name = data.columns[data.order[0].column].data.replace(/\./g,"__");
        var direction = "";

        if (data.order[0].dir == "desc") { direction = "-"};

        $.get('your/tasty/pie/url?format=json', {
            limit: data.length,
            offset: data.start,
            your_search_field__searchattr: data.search.value,
            order_by: direction +sort_column_name
        }, function(res) {
            callback({
                recordsTotal: res.meta.total_count,
                recordsFiltered: res.meta.total_count,
                data: res.objects
            });
        });
    },
    "columns": [
        { "data":"column_1", "orderable": false },
        { "data":"column_2" },
        { "data":"column_3" }
    ],
    "order": [[1, "asc"]]
});
Spec answered 2/8, 2016 at 19:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.