2016 年 6 月 16 日星期四

动态枚举排序

数据的排序是交互式表格中至关重要的一面,是 DataTables 核心所在,让终端用户可以轻松地查找到他们正在寻找的数据。DataTables 针对数字、货币、日期和字符串,内置多种排序类型,但数据是复杂的,DataTables 无法为所有数据集定义排序选项。因此,它提供一个 插件接口,可以让您为数据自定义排序规则。

在这篇文章中,我将介绍一个功能,它将为 枚举列表(通常缩写为 枚举)动态创建排序和类型检测插件。

这个简单的示例表格演示了使用插件直观地对优先级列进行排序

项目名称 优先级
Skilled Drill
Yellow Sleepy Uranium
Massive Tungsten
Dusty Scissors
大院
Moving Backpack
Risky Cloud

为什么对枚举进行排序?

表格中所示数据最终可能只是字符串,但这些字符串传达了大量我们直观理解的信息。例如,如果我们考虑以下三个数据点

我们直观地看到了与它们相关联的优先级顺序。然而,如果它们作为字符串排序,则会被显示为:,这种方式会令终端用户困惑。使用枚举排序插件,我们可以告诉 DataTables 我们希望如何对数据排序。

动态解决方案的需求

编写一个能处理已知枚举和硬编码排序值的插件(比如 "高" = 1"中" = 2 等)很简单,但是我们总是遵循 不要重复自己 (DRY) 原则,以保持代码简洁、干净且易于管理,因此我们需要一个接受开发人员所需的任何枚举的插件。

因此,我们的软件的目标是使用一个函数,它将接受一个值数组,分别定义枚举中的值及其顺序(升序 - 降序由 DataTables 自动处理)

DataTable.enum( [ ... ] );

映射

在深入了解代码之前,我们首先考虑如何实现排序和类型检测插件。本质上,我们只需要检查数据点是否在源数组中,如果在,则检查其位置。我们可以使用 Array.prototype.indexOfjQuery 的 $.inArray 来支持较旧的浏览器,但这不可避免地会导致对阵列数据进行循环,这会损害性能。

相反,我们可以创建一个反向映射对象,其中值为属性键,以实现快速查找,属性值为索引。考虑我们的示例 [ '高', '中', '低' ] - 它将变成

{
    "High": 0,
    "Medium": 1,
    "Low": 2
}

此方法的缺点是,在 ES5 中,对象键只能是字符串 - 因此,如果您使用的是无法轻松转换为唯一字符串(.toString())的复杂对象,则可能遇到冲突。 ES6 引入了 Map 对象,旨在应对这种情况。

现代浏览器具有 相当好的 Map 支持,但当然并非我们所有的客户都使用了新版浏览器,因此我们需要根据需要回退到对象。

我们的映射代码因此十分简单,如下所示

var lookup = window.Map ? new Map() : {};

for ( var i=0, ien=arr.length ; i<ien ; i++ ) {
    lookup[ arr[i] ] = i;
}

插件

有了现有的查找映射表,我们现在可以创建针对 DataTables 类型的检测和排序插件。

类型检测

DataTables 的 类型检测插件 附加到了 DataTable.ext.type.detect 数组。这是一个函数数组,DataTables 将遍历此数组,检查列中的数据是否与任何给定的类型匹配。

在我们的案例中,我们只需检查查找对象是否包含 DataTables 正在检查的数据点的任何值即可。

DataTable.ext.type.detect.unshift( function ( d ) {
    return lookup[ d ] !== undefined ?
        name :
        null;
} );

请注意,unshift 用于将类型检测函数的前缀添加到数组的开头,以确保 DataTables 在其他函数之前使用此类型检测。

此外,请注意变量 name 的用法,它为此数据类型提供了独特的名称。它只是一个带有前缀 enum- 的计数器,以确保可以在一个页面上使用多个枚举!

排序插件

排序插件 附加到 DataTable.ext.type.order 对象,使用排序方法将应用到的 type 的名称。DataTables 内置了针对数字和字符串值的排序比较,因此我们所需要做的就是创建一个会返回可排序值的插件,在这种情况中为查找对象中已有的数据点的数组索引

DataTable.ext.type.order[ name+'-pre' ] = function ( d ) {
    return lookup[ d ];
};

将所有内容放在一起

结果就是我们拥有了一个十分简单但用途广泛的插件,该插件可按任何顺序对任何数据进行排序

var unique = 0;

DataTable.enum = function ( arr ) {
    var name = 'enum-'+(unique++);
    var types = DataTable.ext.type;
    var lookup = {};

    for ( var i=0, ien=arr.length ; i<ien ; i++ ) {
        lookup[ arr[i] ] = i;
    }

    // Add type detection
    types.detect.unshift( function ( d ) {
        return lookup[ d ] !== undefined ?
            name :
            null;
    } );

    // Add sorting method
    types.order[ name+'-pre' ] = function ( d ) {
        return lookup[ d ];
    };
};

只需复制代码从上面复制即可使用此插件,或者你可以直接从 DataTales CDN 包含此插件。

JS

反馈

一如既往,我们非常欢迎反馈。如果你看到可以改进的地方,请随意发起新的 论坛讨论 或派生 DataTables 插件 repo 并发送包含你所做的更改的拉取请求。