0x01 緣由
平時較少涉及前端,這次本以為模板中有表單,分頁跳轉搜索功能都比較齊全,可以高枕無憂,但是細看模板中的分頁跳轉是不需要與后台交互的,數據一次性寫在前端,再有前端插件完成分頁。
這種方式肯定是不可行的,這次要做的可以看做資產管理,服務器+主機量級很大,一個機器生成一條 <th>,第一次請求的前端代碼怕是要崩掉。
所以合理采用的還是后端分頁
0x02 分頁方式
團隊項目里看過的幾種后端分頁方式:
1. Paginator + render渲染整個頁面,這種方式比較好理解,但是每一次跳轉分頁,都會替換掉整塊main-content部分。
2. datatables 后端分頁,這種方式按照datatables提供的API填寫相關信息與邏輯,缺點是
1) 受限於這種插件的條條框框,要自定義一些內容有點麻煩。
比如原始datatables中search是實時的,即在search input框中輸入 "123",會進行 "1"、"12"、"123"三次過濾,在后端分頁中,就會發起三次查詢。
所以要自定義搜索,但是form部分是由datatables初始化過程中生成的,找半天沒有找到插入自定義<input>框的接口。所以用jquery遍歷再添加 <input>。
2) 文檔不太清晰
各種API、接口,在文檔中找來找去挺麻煩的。等到整塊寫的差不多的時候,才意外看到官方后端分頁的文檔。
3. 自己編寫form、跳轉、查詢、排序、每頁數量等功能,就是稍顯麻煩,但還是挺靈活的。
0x03 datatables后端分頁流程
由於模板本身的表單是基於datatables的,所以最后還是修改了datatables。
datatables后端分頁的流程大概可以分為四部分:
1. 修改datatables的初始化參數,改為后端分頁,並修改其他設置
2. datatables傳遞數據給服務端
3. 接收服務端數據,比如說取到第10頁的10條json數據
4. 數據渲染至datatables當前頁面
0x04 修改datatables初始化參數
<script type="text/javascript"> $(function () { //提示信息 var lang = { "sProcessing": "處理中...", "sLengthMenu": "每頁 _MENU_ 項", "sZeroRecords": "沒有匹配結果", "sInfo": "當前顯示第 _START_ 至 _END_ 項,共 _TOTAL_ 項。", "sInfoEmpty": "當前顯示第 0 至 0 項,共 0 項", "sInfoFiltered": "(由 _MAX_ 項結果過濾)", "sInfoPostFix": "", "sSearch": "搜索:", "sUrl": "", "sEmptyTable": "表中數據為空", "sLoadingRecords": "載入中...", "sInfoThousands": ",", "oPaginate": { "sFirst": "首頁", "sPrevious": "上頁", "sNext": "下頁", "sLast": "末頁", "sJump": "跳轉" }, "oAria": { "sSortAscending": ": 以升序排列此列", "sSortDescending": ": 以降序排列此列" } }; //初始化表格 var table = $("#dynamic-table").dataTable({ language:lang, //提示信息 autoWidth: false, //禁用自動調整列寬 stripeClasses: ["odd", "even"], //為奇偶行加上樣式,兼容不支持CSS偽類的場合 processing: true, //隱藏加載提示,自行處理 serverSide: true, //啟用服務器端分頁 searching: false, //禁用原生搜索 orderMulti: false, //啟用多列排序 order: [], //取消默認排序查詢,否則復選框一列會出現小箭頭 renderer: "bootstrap", //渲染樣式:Bootstrap和jquery-ui pagingType: "simple_numbers", //分頁樣式:simple,simple_numbers,full,full_numbers columnDefs: [{ "targets": 'nosort', //列的樣式名 "orderable": false //包含上樣式名‘nosort’的禁止排序 }] }).api();//此處需調用api()方法,否則返回的是JQuery對象而不是DataTables的API對象 }); </script>
datatables初始化中,以字典的形式將初始化信息傳遞進去。
其中,language為表單的一些提示信息。這里比較簡單,基本上看看就明白。
所以,現在表單已經初始化成了后端模式了。
接下來,怎么給后端傳遞數據
0x05 前后端數據交互
需要增加的兩個值:ajax 與 column
<script type="text/javascript"> $(function () { //提示信息 var lang = { "sProcessing": "處理中...", "sLengthMenu": "每頁 _MENU_ 項", "sZeroRecords": "沒有匹配結果", "sInfo": "當前顯示第 _START_ 至 _END_ 項,共 _TOTAL_ 項。", "sInfoEmpty": "當前顯示第 0 至 0 項,共 0 項", "sInfoFiltered": "(由 _MAX_ 項結果過濾)", "sInfoPostFix": "", "sSearch": "搜索:", "sUrl": "", "sEmptyTable": "表中數據為空", "sLoadingRecords": "載入中...", "sInfoThousands": ",", "oPaginate": { "sFirst": "首頁", "sPrevious": "上頁", "sNext": "下頁", "sLast": "末頁", "sJump": "跳轉" }, "oAria": { "sSortAscending": ": 以升序排列此列", "sSortDescending": ": 以降序排列此列" } }; //初始化表格 var table = $("#dynamic-table").dataTable({ language:lang, //提示信息 autoWidth: false, //禁用自動調整列寬 stripeClasses: ["odd", "even"], //為奇偶行加上樣式,兼容不支持CSS偽類的場合 processing: true, //隱藏加載提示,自行處理 serverSide: true, //啟用服務器端分頁 searching: false, //禁用原生搜索 orderMulti: false, //啟用多列排序 order: [], //取消默認排序查詢,否則復選框一列會出現小箭頭 renderer: "bootstrap", //渲染樣式:Bootstrap和jquery-ui pagingType: "simple_numbers", //分頁樣式:simple,simple_numbers,full,full_numbers columnDefs: [{ "targets": 'nosort', //列的樣式名 "orderable": false //包含上樣式名‘nosort’的禁止排序 }], ajax: function (data, callback, settings) { //封裝請求參數 console.log(data); var param = {}; param.limit = data.length;//頁面顯示記錄條數,在頁面顯示每頁顯示多少項的時候 param.start = data.start;//開始的記錄序號 param.page = (data.start / data.length)+1;//當前頁碼 param.order = data.order[0] //console.log(param); //ajax請求數據 $.ajax({ type: "GET", url: "http://your_server", cache: false, //禁用緩存 data: param, //傳入組裝的參數 dataType: "json", success: function (result) { console.log(result); //setTimeout僅為測試延遲效果 setTimeout(function () { //封裝返回數據 var returnData = {}; returnData.draw = data.draw;//這里直接自行返回了draw計數器,應該由后台返回 returnData.recordsTotal = result.total;//返回數據全部記錄 returnData.recordsFiltered = result.total;//后台不實現過濾功能,每次查詢均視作全部結果 returnData.data = result.data;//返回的數據列表 //console.log(returnData); //調用DataTables提供的callback方法,代表數據已封裝完成並傳回DataTables進行渲染 //此時的數據需確保正確無誤,異常判斷應在執行此回調前自行處理完畢 callback(returnData); }, 200); } }); }, //列表表頭字段 columns: [ { "data": "ip" }, { "data": "online_state" }, { "data": "machine_id" }, { "data": "location" }, { "data": "agent_state" }, { "data": "system" } ] }).api();//此處需調用api()方法,否則返回的是JQuery對象而不是DataTables的API對象 }); </script>
1. ajax: function(data,callback,settings){}——data
data為datatables本身會提供的數據集合,包含每一列的信息、一頁的數據量(一頁幾行)、查找的值、當前頁等,另外根據datatables的初始化選擇還會包含其他內容,比如,當點擊排序時,data中將包含排序的列、方式。
想要傳遞給服務端的數據,可以在這一處取。但是最好將數據重新封裝一遍再發送給服務器,原因是:
1) 有的數據是不需要的,比如在原生搜索關閉的情況下,data中還是會以后search的信息
2) 有一些自定義的信息,比如自定義搜索框,可以封裝入其中再傳給服務器
3) 整理格式,比如 排序數據 order中, order[0] 包含排序的列和方式,其他沒什么用,所以取出order[0] 封裝再發送,簡潔得多。
數據封裝完畢,則采用ajax的方式將數據發送給服務端。
2. ajax: function(data,callback,settings){}——callback
callback為返回給datatables渲染當前表單的數據,以字典形式。
其中,draw與data中的draw相同。
recordsTotal、recordsFiltered填寫為數據庫總數據量即可,datatables從中獲取數據總量、分頁數量等。
渲染的核心數據 data,是給表單渲染內容用的。
3. columns字段
columns字段的作用是:
將回調函數中的data中的數據,按columns的排序渲染入表單列中,比如這里,第一列為ip,則將data['ip'] 放到第一列中,以此類推。
4. 后端返回什么數據
后端的數據可以根據傳遞給回調函數的數據進行猜測拼接,但是data也是字典格式的,返回的數據用Python可以寫為:
data = [ {'ip':'192.168.1.91','online_state':'運行中','machine_id':'91','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.92','online_state':'運行中','machine_id':'92','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.93','online_state':'運行中','machine_id':'93','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.94','online_state':'運行中','machine_id':'94','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.95','online_state':'運行中','machine_id':'95','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.96','online_state':'運行中','machine_id':'96','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.97','online_state':'運行中','machine_id':'97','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.98','online_state':'運行中','machine_id':'98','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.99','online_state':'運行中','machine_id':'99','location':'58security','agent_state':'OK','system':'windows'}, {'ip':'192.168.1.100','online_state':'運行中','machine_id':'100','location':'58security','agent_state':'OK','system':'windows'}, ] response = {'data':data,'limit':10,'page':10,'total':100} return JsonResponse(response)
在 ajax success中接收這些數據,再進行封裝、傳入datatables回調函數,從而完成渲染。
其中,顯而易見,data以 “字段”:"內容"的形式存在於字典中。
5. 自定義搜索(version2)
(上一次寫的太丑了)
由此,基本已經完成。
但是原生的搜索是不能用了,所以要自定義搜索。
也找不到添加 <input>框的接口,所以,用jquery遍歷搜索再添加<input>元素、綁定事件。
綁定的事件要做什么事呢?
最簡單的就是讓datatables執行一次提交ajax,這里使用 datatables對象的draw()函數。
JS代碼:
function search(){
table.draw();
}
var search_input = $('#search_input_html').text();
$("#dynamic-table_wrapper").find(".row").first().find(".col-sm-6").last().append(search_input);
$('#searcy_type').html($('#search_type').text());
$("#search_button").click(function(){
search();
});
$("#search_key").keydown(function(e){
if(e.keyCode == 13){
search();
}
})
其中插入的代碼用textarea包裹起來,再用val取代碼
<!--ADDING HTML start--> <textarea id="search_input_html" style="display:none"> <div class="my_dataTables_filter"> <div class="span6"> <select id="searcy_type" class="search_type"> <select> <input id="search_key" class="search_space"></input> <button type="button" id="search_button" class="search_button btn">搜索</button> </div> </div> </textarea> <textarea id="search_type" style="display:none"> <option value="ip">ip</option> <option value="online_status">online_status</option> <option value="machine_id">machine_id</option> </textarea> <!--ADDING HTML end-->
樣式CSS:
.my_dataTables_filter { float: right; } .search_space{ height:34px; position: relative; top:2px; left:4px; } .search_button{ width: 50px; height: 34px; color: #fff; /*font-size:20%;*/ letter-spacing: 1px; background: #65CEA7; border-bottom: 2px solid #65CEA7; /*background: #3385ff;*/ /*border-bottom: 2px solid #2d78f4;*/ outline: medium; -webkit-appearance: none; -webkit-border-radius: 0; } .search_type{ height:34px; position: relative; top:2px; left:4px; }
在初始化結束后,加入這段JS代碼(HTML寫在content部分;CSS放在.css中):遍歷、添加元素、綁定事件、事件為datatables發出ajax。
draw()默認參數為true,表明刷新時會跳轉到第一頁重新刷新;draw(false) 則保持在原頁。
同時,搜索的值也要傳遞給服務端:在封裝數據時加入
param.search_key = $("#search_key").val()
參考——重要程度由高到低:
2. 引導我做demo的文章 jQuery Data Tables 插件自定義Ajax分頁實現
一臉懵時可以做一個demo,后端數據返回,form完成,再一步步做轉移。