2017 年 7 月 24 日星期一

深度連結至 DataTables

在論壇中經常出現的一個問題是「如何深度連結至 DataTable?」 這通常是在 SEO 的情境下被問到,您想要確保表格中的所有資料都能被索引,但它也適用於您想要顯示一個預先設定特定搜尋詞的表格的應用程式中。

通常這個問題的答案是使用初始化參數,從搜尋字串設定所需的任何選項,但這是一個很常見的需求,所以我們應該將其一般化。為此,我在此提供一個短腳本,可用於從搜尋字串中提取參數,並使用它們來填充 DataTable。

讓我們首先看看它的實際運作方式 - 請按照下面的連結來觀察示範表格上的行為

姓名職位辦公室年齡開始日期薪資
Tiger Nixon系統架構師愛丁堡612011-04-25$320,800
Garrett Winters會計東京632011-07-25$170,750
Ashton Cox初級技術作者舊金山662009-01-12$86,000
Cedric Kelly資深 Javascript 開發人員愛丁堡222012-03-29$433,060
Airi Satou會計東京332008-11-28$162,700
Brielle Williamson整合專家紐約612012-12-02$372,000
Herrod Chandler銷售助理舊金山592012-08-06$137,500
Rhona Davidson整合專家東京552010-10-14$327,900
Colleen HurstJavascript 開發人員舊金山392009-09-15$205,500
Sonya Frost軟體工程師愛丁堡232008-12-13$103,600
Jena Gaines辦公室經理倫敦302008-12-19$90,560
Quinn Flynn支援主管愛丁堡222013-03-03$342,000
Charde Marshall區域總監舊金山362008-10-16$470,600
Haley Kennedy資深行銷設計師倫敦432012-12-18$313,500
Tatyana Fitzpatrick區域總監倫敦192010-03-17$385,750
Michael Silva行銷設計師倫敦662012-11-27$198,500
Paul Byrd財務長 (CFO)紐約642010-06-09$725,000
Gloria Little系統管理員紐約592009-04-10$237,500
Bradley Greer軟體工程師倫敦412012-10-13$132,000
Dai Rios人事主管愛丁堡352012-09-26$217,500
Jenette Caldwell開發主管紐約302011-09-03$345,000
Yuri Berry行銷長 (CMO)紐約402009-06-25$675,000
Caesar Vance售前支援紐約212011-12-12$106,450
Doris Wilder銷售助理雪梨232010-09-20$85,600
Angelica Ramos執行長 (CEO)倫敦472009-10-09$1,200,000
Gavin Joyce開發人員愛丁堡422010-12-22$92,575
Jennifer Chang區域總監新加坡282010-11-14$357,650
Brenden Wagner軟體工程師舊金山282011-06-07$206,850
Fiona Green營運長 (COO)舊金山482010-03-11$850,000
Shou Itou區域行銷東京202011-08-14$163,000
Michelle House整合專家雪梨372011-06-02$95,400
Suki Burks開發人員倫敦532009-10-22$114,500
Prescott Bartlett技術作者倫敦272011-05-07$145,000
Gavin Cortez團隊領導舊金山222008-10-26$235,500
Martena Mccray售後支援愛丁堡462011-03-09$324,050
Unity Butler行銷設計師舊金山472009-12-09$85,675
Howard Hatfield辦公室經理舊金山512008-12-16$164,500
Hope Fuentes秘書舊金山412010-02-12$109,850
Vivian Harrell財務總監舊金山622009-02-14$452,500
Timothy Mooney辦公室經理倫敦372008-12-11$136,200
Jackson Bradshaw總監紐約652008-09-26$645,750
Olivia Liang支援工程師新加坡642011-02-03$234,500
Bruno Nash軟體工程師倫敦382011-05-03$163,500
Sakura Yamamoto支援工程師東京372009-08-19$139,575
Thor Walton開發人員紐約612013-08-11$98,540
Finn Camacho支援工程師舊金山472009-07-07$87,500
Serge Baldwin資料協調員新加坡642012-04-09$138,575
Zenaida Frank軟體工程師紐約632010-01-04$125,250
Zorita Serrano軟體工程師舊金山562012-06-01$115,000
Jennifer Acosta初級 Javascript 開發人員愛丁堡432013-02-01$75,650
Cara Stevens銷售助理紐約462011-12-06$145,600
Hermione Butler區域總監倫敦472011-03-21$356,250
Lael Greer系統管理員倫敦212009-02-27$103,500
Jonas Alexander開發人員舊金山302010-07-14$86,500
Shad Decker區域總監愛丁堡512008-11-13$183,000
Michael BruceJavascript 開發人員新加坡292011-06-27$183,000
Donna Snider客戶支援紐約272011-01-25$112,000
姓名職位辦公室年齡開始日期薪資

這個範例的程式碼很簡單

$('#myTable').DataTable( $.fn.dataTable.ext.deepLink( [
    'search.search', 'order', 'displayStart'
] ) );

您還需要在您的頁面上加入深度連結腳本

JS

使用方式

在您自己的頁面上使用此腳本的關鍵元素是 $.fn.dataTable.ext.deepLink 函數。它接受一個參數:您希望搜尋參數指定的選項陣列,並將返回一個包含這些初始化選項的物件,該物件可以直接傳遞給 DataTable,如上面的範例所示。

使用白名單方法來限制哪些選項可以被使用,以確保安全性。例如,您不太可能希望允許使用者修改 ajaxserverSidescrollY 參數。如果給定的參數不在白名單中,則會被忽略。話雖如此,如果您在希望可以使用任何和所有參數的環境中使用您的應用程式,您可以指定 all 作為該函數的唯一參數,這將允許所有參數。

一個簡單的範例:允許從搜尋字串設定 search.search 參數

$('#myTable').DataTable( $.fn.dataTable.ext.deepLink( [
    'search.search'
] );

擴充預設值

由於 deepLink() 函數只會返回一個物件,因此您可以提供預設值,這些預設值可以選擇性地被搜尋字串覆寫,反之亦然,可以使用一個可選擇的參數來覆寫搜尋字串中的任何參數。這可以使用 jQuery.extend() 函數和返回的物件來完成

var searchOptions = $.fn.dataTable.ext.deepLink( [
    'order'
];
var defaultOptions = {
    order: [[ 2, 'desc' ]]
};

$('#myTable').DataTable(
    $.extend( defaultOptions, searchOptions )
);

它是如何運作的?

現在我們知道如何使用它,如果您對它的運作方式感興趣,請繼續閱讀。它其實非常簡單!讓我們首先回到我在論壇中的標準回應,即應該使用搜尋字串來設定參數值。如果我們假設整個搜尋字串將用於搜尋表格 (例如 /table?mySearchTerm),我們可以這樣做

$('#myTable').DataTable( {
    search: {
        search: location.search.replace(/^\?/, '')
    }
} );

現在的關鍵是將搜尋字串分解為鍵值對。令人有點驚訝的是,瀏覽器中沒有用於此目的的 API,但是使用 String.prototype.splitdecodeURIComponent() 來執行此操作非常簡單

$.fn.dataTable.ext.deepLink = function(whitelist) {
    var search = location.search.replace(/^\?/, '').split('&');
    var out = {};

    for (var i = 0, ien = search.length; i < ien; i++) {
        var pair = search[i].split('=');
        var key = decodeURIComponent(pair[0]);
        var value = decodeURIComponent(pair[1]);

        ...
    }

    return out;
};


### Casting values

The query string can only directly hold string values (it is a string itself after all), unless we use a data schema such as JSON. JSON isn't pretty to look at in a URL, so we want to avoid that here. The type always being a string is an issue in this case as certain DataTables parameters expect other types - for example `-init displayStart` must be a `Number` and not a `String` while `-init paging` is a Boolean. As such we need to [cast](https://en.wikipedia.org/wiki/Type_conversion) the string values into their typed counterparts:

```js
if (value === 'true') {
    value = true;
}
else if (value === 'false') {
    value = false;
}
else if (!value.match(/[^\d]/)) {
    value = value * 1;
}
else if (value.indexOf('{') === 0 || value.indexOf('[') === 0) {
    // Try to JSON parse for arrays and obejcts
    try {
        value = $.parseJSON( value );
    }
    catch(e){}
}

這樣做的缺點是,不可能將 search.search 設定為 true,因為它會被上面的程式碼轉換為布林值。我覺得這是一個相對不太可能遇到的情況,但如果您真的遇到了,則需要修改上面的程式碼以滿足您的需求。

寫入巢狀值

您可能已經注意到在上面的範例中,search.search 被大量使用,因為這是我預期在此腳本中最常用的參數之一。但是,它的 Javascript 物件版本實際上是 { search: { search: ... } } - 即一個巢狀物件。要從字串表示法轉換,我們可以使用 DataTables 內建的函數,基於點分隔的字串來建立巢狀物件:$.fn.dataTable.ext.internal._fnSetObjectDataFn (它用於 columns.data)。此函數不是一個已記載的 API,但是下一個主要版本將會將其提升為 API (儘管具有更易於存取的名稱),因為它對於外掛程式和附加腳本非常有用!

var setBuilder = $.fn.dataTable.ext.internal._fnSetObjectDataFn;

if (whitelist === 'all' || $.inArray(key, whitelist) !== -1) {
    var setter = setBuilder(key);
    setter(out, value);
}

原始碼和 Git

此腳本的完整文件化原始碼可以在 GitHub 上找到。如果您對如何增強它有任何想法,歡迎提出 pull request!