최근 사용하게 된 datatables 라이브러리에 대한 약간의 설명을 공유하고자 합니다.
datatables는 데이터 그리드를 다루는 라이브러리로 '이 기능이 있었으면' 하는 부분은 거의 갖추고 있습니다.
한글로 된 자료는 거의 기본옵션에 대한 설명밖에 없어 고생했는데(ㅠㅠ), 실제 구현했던 기능들 위주로 설명할 예정입니다.
Datatables 는 MIT 라이센스로 무료입니다.
DEMO : https://saintsilver.github.io/datatables-ex/
기본 사용
윗 스크린샷에서 설명이 생략된 #myTable 은 아래와 같은 구조로 만드시면 되고,
서버사이드로 데이터를 처리하여 데이터테이블을 만들 시 <tbody>를 선언하시고 실제로 값을 넣으시면 됩니다.
저는 ajax로 처리할 예정이라서 <thead>만 사용했습니다.
공식홈페이지에선 데이터가 100,000건 이상이면 서버사이드로 처리하라고 권장하고 있습니다.
<table id="myTable"> <thead> <tr> <th>컬럼1</th> <th>컬럼2</th> <th>컬럼3</th> </tr> </thead> </table> | cs |
Ajax
var table = $('#myTable').DataTable({ ajax: { 'url':'MOCK_DATA.json', //'type': 'POST', 'dataSrc':'' }, columns: [ {"data": "id"}, {"data": "first_name"}, {"data": "last_name"}, {"data": "email"} ] }); | cs |
{ "data": [ { "id": "1", "name": "Tiger Nixon", "position": "System Architect", "salary": "$320,800", "start_date": "2011/04/25", "office": "Edinburgh", "extn": "5421" }, { "id": "2", "name": "Garrett Winters", "position": "Accountant", "salary": "$170,750", "start_date": "2011/07/25", "office": "Tokyo", "extn": "8422" } .... | cs |
스타일
https://datatables.net/examples/styling/material.html
https://datatables.net/examples/styling/uikit.html
언어 변경
var table = $('#myTable').DataTable({ "language": { "emptyTable": "데이터가 없어요.", "lengthMenu": "페이지당 _MENU_ 개씩 보기", "info": "현재 _START_ - _END_ / _TOTAL_건", "infoEmpty": "데이터 없음", "infoFiltered": "( _MAX_건의 데이터에서 필터링됨 )", "search": "에서 검색: ", "zeroRecords": "일치하는 데이터가 없어요.", "loadingRecords": "로딩중...", "processing": "잠시만 기다려 주세요...", "paginate": { "next": "다음", "previous": "이전" } }, }); | cs |
옵션이 엄청나게 많은데, 이 정도만 바꾸셔도 영어는 안 보일 겁니다.
아래에서 더 찾아보실 수 있습니다.
https://datatables.net/reference/option/language
셀 데이터 커스텀
....생략 columns: [ {"data": "id"}, .... {"data": "url", "render": function(data, type, row){ if(type=='display'){ data = '<a href="'+ data + '">' + 링크로 이동 + '</a>'; } return data; } ] .... }}, | cs |
반응형
그리고 선언할 때 옵션 하나만 더해주시면 됩니다.
$('#myTable').DataTable( { responsive: true } ); | cs |
다만 테이블 크기가 처음 켜질 때의 브라우저 크기에 맞춰 고정되는 것으로 보입니다.
그래서 저는 table 자체에 추가로 width: 100% 를 주고 사용합니다.
컬럼별 검색
/* Column별 검색기능 추가 */ $('#myTable_filter').prepend('<select id="select"></select>'); $('#myTable > thead > tr').children().each(function (indexInArray, valueOfElement) { $('#select').append('<option>'+valueOfElement.innerHTML+'</option>'); }); | cs |
그리고 아래와 같이 원래 있던 검색기능을 unbind 한 뒤에, select 옵션으로 선택된 컬럼만 검색하도록 다시 이벤트를 추가했습니다. $('.dataTables_filter input') 은 검색 input 이고, draw()는 키가 눌릴 때마다 테이블을 다시 그려 줍니다.
(이렇게 변경됐습니다)
$('.dataTables_filter input').unbind().bind('keyup', function () { var colIndex = document.querySelector('#select').selectedIndex; table.column(colIndex).search(this.value).draw(); }); | cs |
날짜 기간 검색
$('#myTable_filter').prepend('<input type="text" id="toDate" placeholder="yyyy-MM-dd"> '); $('#myTable_filter').prepend('<input type="text" id="fromDate" placeholder="yyyy-MM-dd">~'); | cs |
여기가 좀 까다로운데요, datatable을 선언하기 전에 search 기능에 약간 손을 봐 줍시다.
조건이 4개인 이유는, ~부터 혹은 ~까지 중 하나만 입력해도 검색이 이루어지게 하기 위함입니다.
https://datatables.net/manual/plug-ins/search 여기를 참고하실 수 있습니다.
true가 리턴되면 검색이 이뤄지구요, false가 리턴되면 무시합니다.
$.fn.dataTable.ext.search.push( function(settings, data, dataIndex){ var min = Date.parse($('#fromDate').val()); var max = Date.parse($('#toDate').val()); var targetDate = Date.parse(data[5]); if( (isNaN(min) && isNaN(max) ) || (isNaN(min) && targetDate <= max )|| ( min <= targetDate && isNaN(max) ) || ( targetDate >= min && targetDate <= max) ){ return true; } return false; } ) | cs |
분명히 맞게 쓴 것 같은데 작동을 안해서 삽질을 했는데, 위에서 컬럼별 검색기능을 추가하면서 검색기능이 겹쳤습니다.
아래와 같이 날짜를 입력하는 input 에만 이벤트를 리바인딩 처리했습니다.
$('#toDate, #fromDate').unbind().bind('keyup',function(){ table.draw(); }) | cs |
footer와 summary
<tfoot> <tr> <th colspan="2" style="text-align:right;white-space:nowrap;">TOTAL : </th> <th colspan="6" style="text-align:left;white-space:nowrap;"></th> </tr> </tfoot> | cs |
/* Footer에 금액총합 구하기, * filtered data 총합만 계산하도록 함.*/ "footerCallback":function(){ var api = this.api(), data; var result = 0; api.column(7, {search:'applied'}).data().each(function(data,index){ result += parseFloat(data); }); $(api.column(3).footer()).html(result.toLocaleString()+'원'); }, | cs |
.toLocaleString()은 천단위로 컴마를 찍어줍니다.
그리고 해당 콜백메서드는 5개의 파라미터를 받을 수 있습니다. 저는 사용하지 않았는데 아래에서 확인해 보세요.
https://datatables.net/reference/option/footerCallback
아래와 같이 필터링 된 데이터의 금액총합을 구했습니다.
(아름답습니다.. )
엑셀Excel로 Export
- 클립보드로 복사하기
- CSV 파일
- Excel 파일
- PDF 파일
- 프린트
다만 저 기능을 다 가져오려면 추가되는 파일이 좀 많은데요, 저는 CSV 파일로만 export 하는 걸로 하겠습니다.
그럼 아래 두 개만 추가하시면 됩니다.
https://cdn.datatables.net/buttons/1.5.2/js/dataTables.buttons.min.js그리고 아래와 같이 사용합니다.
$(document).ready(function() { $('#myTable').DataTable( { dom: 'Blfrtip', buttons: [{ extend: 'csvHtml5', text: 'Export CSV', footer: true, className: 'exportBtn' }] } ); } ); | cs |
이렇게 선언하는 것만으로 버튼을 누르자마자 footer가 포함된 데이터가 (그것도 필터링된!) csv 파일로 다운로드됩니다.
dom 옵션의 Blfrtip 은 단어가 아니라, 알파벳 하나하나에 의미가 있어 옵션을 주는 방식입니다.
아래와 같은데요, 비워놓을시 디폴트값은 lfrtip 입니다. 여기에 Buttons의 B 가 추가되는 것이라고 보면 됩니다.
순서를 바꾸면 위치도 바꿀 수 있습니다. 예를 들어 f 를 p 바로 이전에 두면 페이징 위에 필터링 input이 위치합니다.
l
-l
ength changing input controlf
-f
iltering inputt
- Thet
able!i
- Tablei
nformation summaryp
-p
agination controlr
- pr
ocessing display element
아래와 같이 필터링 된 검색결과에서 엑셀버튼을 누르면,
다운로드된 파일에서 필터링 된 결과와 동일한 데이터를 담은 csv 파일을 얻을 수 있습니다.
새로고침
1 2 | /* 페이지 고정 */ $('#dataList').DataTable().ajax.reload(null, false); | cs |
기타 기능
이 외에도 하나의 포스팅으로 설명하기 어려운 만큼 다양한 옵션이 있습니다.
orderMulti: true 를 설정하면 쉬프트키를 이용해 여러 컬럼을 동시에 정렬할 수 있구요.
order : [[index, 'desc']] 와 같이 설정하면 처음 datatable이 그려질 때 선택한 컬럼이 역순으로 정렬됩니다.
헤더/컬럼/푸터 고정, Row grouping, 리오더, 스크롤러 등..
이번에 사용하진 않았지만 Editor 기능을 통해 테이블에서 CRUD 도 가능합니다. (이 기능은 유료입니다)
아래 링크에서 포스팅에 사용한 코드를 보실 수 있습니다.