65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2014 年 6 月 25 日

CPOL

5分钟阅读

viewsIcon

22412

downloadIcon

549

功能齐全但代码简短(仅 436 行)的 Datatables 替代品

引言

有一天,在花了更多时间尝试进一步自定义我的 knockout 绑定以用于 datatables 后,我受够了!并决定编写自己的、对 knockout 友好的 datatable。
我很高兴我做到了!只花了 12 个小时和 436 行代码!而且,在这个阶段,它很容易自定义!
在这里,为了您的 Web 开发利益!

背景

对于我们的业务应用程序,我们需要一个外观精美的网格,可以进行排序和过滤。为了更好的用户体验,它应该在客户端进行。为了更好的开发体验,它应该易于设置(即,只需指定每列要显示的成员字段)并且灵活(或提供一个函数甚至一个 knockout 模板)。列应该是可隐藏的。理想情况下,它应该是数据驱动的(即,没有 jQuery 事件,只需在 JavaScript 中设置控件的属性,网格就会更新!这被称为 MVVM 编程)。

我决定依赖此控件的技术是 HTMLTypeScript(这是一个 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 模板,显示任何内容。数据上下文将是单元格数据
    • cellStylestringobservable<string>,用于 TD 的样式
    • headerStylestringobservable<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:最初发布
© . All rights reserved.