经过过滤、分页、排序的可自定义客户端表格





5.00/5 (5投票s)
功能齐全但代码简短(仅 436 行)的 Datatables 替代品
引言
有一天,在花了更多时间尝试进一步自定义我的 knockout 绑定以用于 datatables 后,我受够了!并决定编写自己的、对 knockout 友好的 datatable。
我很高兴我做到了!只花了 12 个小时和 436 行代码!而且,在这个阶段,它很容易自定义!
在这里,为了您的 Web 开发利益!
背景
对于我们的业务应用程序,我们需要一个外观精美的网格,可以进行排序和过滤。为了更好的用户体验,它应该在客户端进行。为了更好的开发体验,它应该易于设置(即,只需指定每列要显示的成员字段)并且灵活(或提供一个函数甚至一个 knockout 模板)。列应该是可隐藏的。理想情况下,它应该是数据驱动的(即,没有 jQuery 事件,只需在 JavaScript 中设置控件的属性,网格就会更新!这被称为 MVVM 编程)。
我决定依赖此控件的技术是 HTML 和 TypeScript(这是一个 Web 应用程序!),Bootstrap(让表格看起来不错),以及 Knockout(以实现 MVVM 开发风格)。
我的起点是 Knockout 示例部分的 分页网格示例,它在很大程度上为我提供了灵感。
虽然我使用 VS2013 Update 2 作为我的 IDE 开发了这个组件(其中包含 TypeScript 编译器),但该组件已准备就绪。双击 TestPage.html 即可启动它。如果您无法或不愿使用 TypeScript,KOGridBinding.js(包含在 .zip 文件中)就是您所需要的一切。
项目内容
除了样式文件(这是一个 Web 应用程序!)和测试文件(当然)之外,还有 2 个源文件
- knockout.d.ts,TypeScript Knockout 绑定,来自 NuGet。它唯一的用途是让 TypeScript 完全类型化地调用 Knockout API。它不生成 JavaScript 文件,并且在运行时不使用,仅在编译时使用。
- KOGridBinding.ts,网格的源,共 380 行代码,将在下面简要说明。
或者,您可以跳过 TypeScript 版本,直接使用(预编译的)JavaScript 版本。您只需要 KOGridBinding.js(已包含)和样式文件。
Using the Code
要使用该网格,只需在适当样式化的 TABLE
标签上调用“kogrid
”绑定,如
<table class="table table-striped table-bordered table-hover table-condensed"
data-bind='kogrid: gridViewModel'> </table>
这里的类用于 bootstrap 表格样式。传递给 kogrid
绑定的数据必须是 KOGridModel
(如 KOGridBinding.ts 中定义),否则将抛出异常作为提醒。
KOGridModel
类拥有描述网格状态的所有属性。
class KOGridColumn {
header: string = null; // REMARK: to prevent event bubbling:
// data-bind="event: { click: function() {} }, clickBubble: false"
headerTemplate: string = null;
data: (row: any) => any = null; // function to get data for row
template: string = null; // template taking the row as parameter
dataTemplate: string = null; // template taking the cell data as value property, i.e. { value: data }
cellStyle: (row) => any = null; // add style to the TD
headerStyle: any; // add style to the TH visible = ko.observable(true);
sortable = ko.observable(true);
sort = ko.observable(KOGridSortOrder.None);
constructor(config?: IKOGridColumn) {
// code removed for clarity
}
}
class KOGridModel {
// data
columns: Array<KOGridColumn> = []; // REMARK: set that before itemsSource, or call update()
itemsSource = ko.observableArray();
// UI
inputClass = ko.observable<string>('form-control inline small');
// manipulation
paginations = ko.observableArray([5, 10, 25, 50, 100]);
pageSize = ko.observable(10);
currentPage = ko.observable(1);
filter = ko.observable<string>();
sortColumn = ko.observable<number>();
// templates for rendering
templateTable = "KOGridModelDefaultViewTemplate";
templateHeader = "KOGridModelDefaultViewHeaderTemplate";
// computed
currentItems: KnockoutComputed<Array<any>>;
currentPageItems: KnockoutComputed<Array<any>>;
maxPage: KnockoutComputed<number>;
numItems: KnockoutComputed<number>;
constructor(config?: IKOGridConfig) {
// code removed for clarity...
}
// force new evaluation of computed variables
update() {
}
// as the name say
sort(column: number) {
}
// change currentPage
moveTo(dst: KOGridNavigation) {
// code removed for clarity...
}
// set some default columns properties from the data
scaffold(data){
// code removed for clarity...
}
}
itemsSource
:包含显示的所有行columns
:描述每一列header
:列标题(string
)headerTemplate
:而不是string
,传递一个 knockout 模板来显示任何内容,甚至是 HTML 输入!data
:Function(row),获取单元格数据template
:使用ko
模板,显示任何内容。数据上下文将是行dataTemplate
:使用ko
模板,显示任何内容。数据上下文将是单元格数据cellStyle
:string
或observable<string>
,用于TD
的样式headerStyle
:string
或observable<string>
,用于TH
的标题样式visible
:列是否可见sortable
:列是否可排序(还需要数据不为null
)sort
:UI 状态,当前排序状态
inputClass
:将应用于搜索和分页控件的类paginations
:可能的页面大小值pageSize
:当前页面大小(从 paginations 中选择)currentPage
:UI 状态,显示的数据的当前页filter
:UI,用于过滤数据的string
sortColumn
:当前排序的列templateTable
:用于渲染此模型的 KO 模板。提供了一个默认模板(如图所示)templateHeader
:用于渲染控件头部(搜索和分页)的 KO 模板,提供了 2 个默认模板
此外,一些 UI 状态属性会根据以下组合自动计算:filter + sortColumn + itemsSource + currentPage + pageSize
currentItems
:过滤和排序后的itemsSource
currentPageItems
:当前页上的项目(来自currentItems
)maxPage
:页数,即currentItems
除以pageSize
的结果numItems
:过滤后的项目数
网格模型可以使用类似 JSON 对象的模型进行初始化,并将填充缺失的属性,例如示例应用程序中的代码
this.gridViewModel = new KOGridModel({
data: this.items,
columns: [
{ header: "Item Name", data: "name" },
{ header: "Sales Count", data: "sales" },
{ header: "Price", data: function (item) { return "$" + parseFloat(item.price()).toFixed(2) } },
{ headerTemplate: "headerPrice", template: "columnPrice", visible: this.showPriceEdit }
],
pageSize: 4
});
关注点
这段代码是一个很好的起点,可以查看一个真实、但简单的自定义 knockout 绑定。
原始 ko 示例中的 AddTemplate()
函数给了我一个新颖的想法,即从 JavaScript 提供我的模板!
function addTemplate(name, markup) {
document.write("<script type='text/html' id='" + name + "'>" + markup + "<" + "/script>");
};
计算!我真的需要强调这一点,**计算**!它们太棒了!不要订阅可观察对象的更改(通过 subscribe()
方法),只需使用计算函数。如果它们使用的任何可观察对象发生变化,它们会自动被调用。
巧妙使用 Bootstrap 来获得外观精美的控件(看看搜索和分页头部就知道了)是必须的。
我必须解决当它们使用非可观察对象时重新计算可观察对象的问题(例如,当有人设置 itemsSource
而不是 itemsSource()
来与模型共享 items
数组时)。看看我是如何实现 update()
的。有一个隐藏的可观察对象在 update()
中更改,并在 currentItems()
计算中请求。
摘要
在这里,我介绍了一个新的 Knockout 绑定扩展:“kogrid
”及其关联的数据模型“KOGridModel
”。它们结合起来,可以非常轻松地创建外观精美、可自定义、可过滤、可排序的 datagrid
。
历史
- 2014/06/27:经过两天大量使用后,我发现了一些缺失的功能和错误。例如自定义 TD 样式、更漂亮的分页控件、布尔值排序、对排序列或过滤时进行批量操作的缓慢、开箱即用的示例(无需 VS)。最新版本修复了这些问题!
- 2014/06/25:最初发布