DataTables(+Django)でAjax通信

はじめに

DataTables 1.10.12 (https://datatables.net/)でAjax通信を行う方法をまとめます。
以降のサンプルプログラムではDjangoを使用しています。

DataTablesの設置

まず、次のテーブルをWebページに設置します。
デザインにはBootstrapを使用しています。

f:id:hiroki-sawano:20181121002412p:plain

table 要素の idmytable とし、ヘッダのみ作成しておきます。

<table id="mytable" class="table table-striped table-bordered table-hover dataTable no-footer dtr-inline">
    <thead>
        <tr>
            <th>Column1</th>
            <th>Column2</th><th>Column10</th>
        </tr>
    </thead>
</table>

続けて、DataTableの初期化を行います。
$(document).ready() で各種設定を行います。

$('#mytable').DataTable({
  scrollX: true,
  ordering: false,
  displayLength: 25,
  deferRender: true,
  ajax: {
    "processing": true,
    "url": "{% url 'myapp:action' %}",
    "data": function( d ) {
      d.param1 = $('#id_param1').val();
      d.param2 = $('#id_param2').val();
      d.param3 = $('#id_param3').val();
    },
    "dataSrc": ""
  },
  columns: [
    { "data": "item1",
      "render": function (data, type, full, meta) {
        return '<a href="/link/to/another/page?param=' + full.item1 + '">' + full.item1 + '</a>';
      }
    },
    { "data": "item2" },
    { "data": "item3" },
    { "data": "item4" },
    { "data": "item5" },
    { "data": "item6" },
    { "data": "item7" },
    { "data": "item8" },
    { "data": "item9" },
    { "data": "item10" }
  ]
});

ajax オプション

url ではリクエスト先のURLを指定します(例ではDjangoテンプレートの記法で記述)。
data ではGET時のパラメータを設定しています。

columns オプション

DataTableの各列に表示する値を設定します。
基本的な使い方として{ "data": "key" }を列の数だけ用意しておき、 keyJSONデータのキーを指定します。 例えば [{"key": 100}] というデータがサーバから返却されたとき、 対象の列には100が設定されます。
単純に値を出力するだけでなく、例えばその値を用いてハイパーリンクを構成したり、 画像へのリンクを生成したい場合には、前述の例に示したように render オプションを使用します。

deferRender オプション

多量データを処理する場合には deferRender: true を設定すると性能が向上します。
このオプションを有効にすることでデータテーブルは実際に画面表示に必要な要素のみを読み込むため、 数万件のレコードを受信した場合でも、ページングの都度必要な件数のみを処理することができます。

Ajax通信

f:id:hiroki-sawano:20181121012614g:plain

クライアントサイド

myapp:action へのGETリクエストで設定するパラメータを入力するフォームを設置します。

<form id="myform" role="form">
  <div class="form-group">
    <label>Param1</label>
    <input type="text" name="column1" class="form-control" id="id_param1">
  </div>
  <div class="form-group">
    <label>Param2</label>
    <input type="text" name="column2" class="form-control" id="id_param2">
  </div>
  <div class="form-group">
    <label>Param3</label>
    <input type="text" name="column3" class="form-control" id="id_param3">
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>

Submit ボタン押下時のコールバックでは DataTable().ajax.reload() によってサーバとのAjax通信を開始します。

$("#myform").submit(function(event) {
  event.preventDefault();
  $('#mytable').DataTable().ajax.reload();
  return false;
});

このときデータテーブルでは上記テキストボックスに入力した値を クエリストリングに設定しています。

従ってURLは /myapp/action?param1=foo&param2=bar&param3=baz のような形式になります。

(再掲)

  ajax: {"data": function( d ) {
      d.param1 = $('#id_param1').val();
      d.param2 = $('#id_param2').val();
      d.param3 = $('#id_param3').val();
    },
    …
  },

medium.com

サーバサイド

サーバサイドの実装は今回主題ではないため、簡単に固定のJSONを返却しておきます。
ここで param1 の設定がない場合をページロード時の通信であると判断して空のJSONを返却しています。
どうやら serverSide : truedeferLoading : 0 にすると初回のAjax通信を抑止できるようですが試してません。

def action(request):
    if request.GET.get("param1"):
        response = '[{"item1":1, "item2":2, "item3":3, "item4":4, "item5":5, "item6":6, "item7":7, "item8":8, "item9":9, "item10":10}]'
        return HttpResponse(response, content_type='application/json')
    else:
        # Request on page load
        return HttpResponse('{}', content_type='application/json')

stackoverflow.com

Ajaxリロード時のプログレス表示

processing : true であるにも関わらず、 DataTable().ajax.reload()Loading... が表示されない問題が生じたため次のコードで対処しました。

$('#mytable').on('preXhr.dt', function(e, settings, data){
  $(this).DataTable().clear();
  settings.iDraw = 0;
  $(this).DataTable().draw();
});

stackoverflow.com

エラー処理

Ajax通信のエラー処理を実装する場合には、 xhr.dt イベントでHTTPステータス xhr.status を判定し、 サーバサイドから返却された xhr.responseJSON 内のエラーメッセージ等を表示、 DataTable().clear().draw() で空のデータテーブルを再描画すると良さそうです。

$('#mytable').on('xhr.dt', function ( e, settings, json, xhr ) {
  // Receive BadRequest status
  if (xhr.status == '400') {
    // Show error message
    $("#err_msg").text(xhr.responseJSON.error[0].message);
    $("#err_msg").show();
    // Redraw data table
    $('#mytable').DataTable().clear().draw();
    // Prevent error.dt from occurring
    return true;
  }
});

qiita.com

(2020/07/26追記) ヘッダ・ボディ間のスペース除去

scrollX: true を設定するとヘッダとボディの間にスペースが生じます。
DataTableで scrollCollapse: true を設定し、以下のスタイルを適用すると除去できます。

$('#mytable').DataTable({
  scrollX: true,
  scrollCollapse: true, // 追加
  // ...
<style>
.dataTables_scrollBody thead {
    visibility: collapse !important;
}
.dataTables_scrollBody {
  margin-top: -20px;
}
</style>

stackoverflow.com